home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / tde3.zip / BLOCK.C < prev    next >
C/C++ Source or Header  |  1993-06-05  |  92KB  |  2,841 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - block commands module
  10.  * Purpose: This file contains all the commands than manipulate blocks.
  11.  * File:    block.c
  12.  * Author:  Douglas Thomson
  13.  * System:  this file is intended to be system-independent
  14.  * Date:    October 1, 1989
  15.  */
  16. /*********************  end of original comments   ********************/
  17.  
  18. /*
  19.  * In the DTE editor, Doug only supported functions for STREAM blocks.
  20.  *
  21.  * The block routines have been EXTENSIVELY rewritten.  This editor uses LINE,
  22.  * STREAM, and BOX blocks.  That is, one may mark entire lines, streams of
  23.  * characters, or column blocks.  Block operations are done in place.  There
  24.  * are no paste and cut buffers.  In limited memory situations, larger block
  25.  * operations can be carried out.  Block operations can be done within or
  26.  * across files.
  27.  *
  28.  * In TDE, version 1.1, I separated the BOX and LINE actions.
  29.  *
  30.  * In TDE, version 1.3, I put STREAM blocks back in.  Added block upper case,
  31.  *  block lower case, and block strip high bit.
  32.  *
  33.  * In TDE, version 1.4, I added a block number function.  Here at our lab,
  34.  *  I often need to number samples, lines, etc..., comes in fairly useful.
  35.  *
  36.  * In TDE, version 2.0, I added a box block sort function.
  37.  *
  38.  * In TDE, version 2.0e, I added BlockRot13, BlockFixUUE, and BlockEmailReply.
  39.  *
  40.  * In TDE, version 2.2, the big text buffer was changed to a double linked list.
  41.  *  all characters in the line of each node must be accurately counted.
  42.  *
  43.  * New editor name:  TDE, the Thomson-Davis Editor.
  44.  * Author:           Frank Davis
  45.  * Date:             June 5, 1991, version 1.0
  46.  * Date:             July 29, 1991, version 1.1
  47.  * Date:             October 5, 1991, version 1.2
  48.  * Date:             January 20, 1992, version 1.3
  49.  * Date:             February 17, 1992, version 1.4
  50.  * Date:             April 1, 1992, version 1.5
  51.  * Date:             June 5, 1992, version 2.0
  52.  * Date:             October 31, 1992, version 2.1
  53.  * Date:             April 1, 1993, version 2.2
  54.  * Date:             June 5, 1993, version 3.0
  55.  *
  56.  * This modification of Douglas Thomson's code is released into the
  57.  *  public domain, Frank Davis.  You may distribute it freely.
  58.  */
  59.  
  60. #include "tdestr.h"
  61. #include "common.h"
  62. #include "tdefunc.h"
  63. #include "define.h"
  64.  
  65.  
  66. /*
  67.  * Name:    mark_block
  68.  * Class:   primary editor function
  69.  * Purpose: To record the position of the start of the block in the file.
  70.  * Date:    June 5, 1991
  71.  * Passed:  window:  pointer to current window
  72.  * Notes:   Assume the user will mark begin and end of a block in either
  73.  *           line, stream, or box mode.  If the user mixes types, then block
  74.  *           type defaults to current block type.
  75.  */
  76. int  mark_block( WINDOW *window )
  77. {
  78. int type;
  79. int num;
  80. long lnum;
  81. register file_infos *file;      /* temporary file variable */
  82. register WINDOW *win;           /* put window pointer in a register */
  83. int rc;
  84.  
  85.    win  = window;
  86.    file = win->file_info;
  87.    if (win->rline > file->length || win->ll->len == EOF)
  88.       return( ERROR );
  89.    if (g_status.marked == FALSE) {
  90.       g_status.marked = TRUE;
  91.       g_status.marked_file = file;
  92.    }
  93.    if (g_status.command == MarkBox)
  94.       type = BOX;
  95.    else if (g_status.command == MarkLine)
  96.       type = LINE;
  97.    else if (g_status.command == MarkStream)
  98.       type = STREAM;
  99.    else
  100.       return( ERROR );
  101.  
  102.    rc = OK;
  103.    /*
  104.     * define blocks for only one file.  it is ok to modify blocks in any window
  105.     * pointing to original marked file.
  106.     */
  107.    if (file == g_status.marked_file) {
  108.  
  109.       /*
  110.        * mark beginning and ending column regardless of block mode.
  111.        */
  112.       if (file->block_type == NOTMARKED) {
  113.          file->block_ec  = file->block_bc = win->rcol;
  114.          file->block_er  = file->block_br = win->rline;
  115.       } else {
  116.          if (file->block_br > win->rline) {
  117.             file->block_br = win->rline;
  118.             if (file->block_bc < win->rcol && type != STREAM)
  119.                file->block_ec = win->rcol;
  120.             else
  121.                file->block_bc = win->rcol;
  122.          } else {
  123.             if (type != STREAM) {
  124.                file->block_ec = win->rcol;
  125.                file->block_er = win->rline;
  126.             } else {
  127.                if (win->rline == file->block_br &&
  128.                    win->rline == file->block_er) {
  129.                   if (win->rcol < file->block_bc)
  130.                      file->block_bc = win->rcol;
  131.                   else
  132.                      file->block_ec = win->rcol;
  133.                } else if (win->rline == file->block_br)
  134.                   file->block_bc = win->rcol;
  135.                else {
  136.                   file->block_ec = win->rcol;
  137.                   file->block_er = win->rline;
  138.                }
  139.             }
  140.          }
  141.  
  142.          /*
  143.           * if user marks ending line less than beginning line then switch
  144.           */
  145.          if (file->block_er < file->block_br) {
  146.             lnum = file->block_er;
  147.             file->block_er = file->block_br;
  148.             file->block_br = lnum;
  149.          }
  150.  
  151.          /*
  152.           * if user marks ending column less than beginning column then switch
  153.           */
  154.          if ((file->block_ec < file->block_bc) && (type != STREAM ||
  155.               (type == STREAM && file->block_br == file->block_er))) {
  156.             num = file->block_ec;
  157.             file->block_ec = file->block_bc;
  158.             file->block_bc = num;
  159.          }
  160.       }
  161.  
  162.       /*
  163.        * block type in now defined.  if user mixes block types then block
  164.        * is defined as current block type.
  165.        */
  166.       if (file->block_type != NOTMARKED) {
  167.          /*
  168.           * if block type goes to BOX, check to make sure ec is greater than
  169.           * or equal to bc.  ec can be less than bc in STREAM blocks.
  170.           */
  171.          if (type == BOX) {
  172.             if (file->block_ec < file->block_bc) {
  173.                num = file->block_ec;
  174.                file->block_ec = file->block_bc;
  175.                file->block_bc = num;
  176.             }
  177.          }
  178.       }
  179.  
  180.       assert( file->block_er >= file->block_br );
  181.  
  182.       file->block_type = type;
  183.       file->dirty = GLOBAL;
  184.    } else {
  185.       /*
  186.        * block already defined
  187.        */
  188.       error( WARNING, win->bottom_line, block1 );
  189.       rc = ERROR;
  190.    }
  191.    return( rc );
  192. }
  193.  
  194.  
  195. /*
  196.  * Name:    unmark_block
  197.  * Class:   primary editor function
  198.  * Purpose: To set all block information to NULL or 0
  199.  * Date:    June 5, 1991
  200.  * Passed:  arg_filler: variable to match array of function pointers prototype
  201.  * Notes:   Reset all block variables if marked, otherwise return.
  202.  *           If a marked block is unmarked then redraw the screen(s).
  203.  */
  204. int  unmark_block( WINDOW *arg_filler )
  205. {
  206. register file_infos *marked_file;
  207.  
  208.    if (g_status.marked == TRUE) {
  209.       marked_file              = g_status.marked_file;
  210.       g_status.marked          = FALSE;
  211.       g_status.marked_file     = NULL;
  212.       marked_file->block_start = NULL;
  213.       marked_file->block_end   = NULL;
  214.       marked_file->block_bc    = marked_file->block_ec = 0;
  215.       marked_file->block_br    = marked_file->block_er = 0l;
  216.       if (marked_file->block_type)
  217.          marked_file->dirty = GLOBAL;
  218.       marked_file->block_type  = NOTMARKED;
  219.    }
  220.    return( OK );
  221. }
  222.  
  223.  
  224. /*
  225.  * Name:    restore_marked_block
  226.  * Class:   helper function
  227.  * Purpose: To restore block beginning and ending row after an editing function
  228.  * Date:    June 5, 1991
  229.  * Passed:  window:  pointer to current window
  230.  *          net_change: number of bytes added or subtracted
  231.  * Notes:   If a change has been made before the marked block then the
  232.  *           beginning and ending row need to be adjusted by the number of
  233.  *           lines added or subtracted from file.
  234.  */
  235. void restore_marked_block( WINDOW *window, int net_change )
  236. {
  237. long length;
  238. register file_infos *marked_file;
  239.  
  240.    if (g_status.marked == TRUE && net_change != 0) {
  241.       marked_file = g_status.marked_file;
  242.       length = marked_file->length;
  243.  
  244.       /*
  245.        * restore is needed only if a block is defined and window->file_info is
  246.        * same as marked file and there was a net change in file length.
  247.        */
  248.       if (marked_file == window->file_info) {
  249.  
  250.          /*
  251.           * if cursor is before marked block then adjust block by net change.
  252.           */
  253.          if (marked_file->block_br > window->rline) {
  254.             marked_file->block_br += net_change;
  255.             marked_file->block_er += net_change;
  256.             marked_file->dirty = GLOBAL;
  257.          /*
  258.           * if cursor is somewhere in marked block don't restore, do redisplay
  259.           */
  260.          } else if (marked_file->block_er >= window->rline)
  261.             marked_file->dirty = GLOBAL;
  262.  
  263.          /*
  264.           * check for lines of marked block beyond end of file
  265.           */
  266.          if (marked_file->block_br > length)
  267.             unmark_block( window );
  268.          else if (marked_file->block_er > length) {
  269.             marked_file->block_er = length;
  270.             marked_file->dirty = GLOBAL;
  271.          }
  272.       }
  273.    }
  274. }
  275.  
  276.  
  277. /*
  278.  * Name:    prepare_block
  279.  * Class:   helper function
  280.  * Purpose: To prepare a window/file for a block read, move or copy.
  281.  * Date:    June 5, 1991
  282.  * Passed:  window:  pointer to current window
  283.  *          file: pointer to file information.
  284.  *          text_line: pointer to line in file to prepare.
  285.  *          lend: line length.
  286.  *          bc: beginning column of BOX.
  287.  * Notes:   The main complication is that the cursor may be beyond the end
  288.  *           of the current line, in which case extra padding spaces have
  289.  *           to be added before the block operation can take place.
  290.  *           this only occurs in BOX and STREAM operations.
  291.  *          since we are padding a line, do not trim trailing space.
  292.  */
  293. int  prepare_block( WINDOW *window, line_list_ptr ll, int bc )
  294. {
  295. register int pad = 0;   /* amount of padding to be added */
  296. register int len;
  297.  
  298.    assert( bc >= 0 );
  299.    assert( bc < MAX_LINE_LENGTH );
  300.    assert( ll->len != EOF );
  301.    assert( g_status.copied == FALSE );
  302.  
  303.    copy_line( ll );
  304.    detab_linebuff( );
  305.  
  306.    len = g_status.line_buff_len;
  307.    pad = bc - len;
  308.    if (pad > 0) {
  309.       /*
  310.        * insert the padding spaces
  311.        */
  312.  
  313.       assert( pad >= 0 );
  314.       assert( pad < MAX_LINE_LENGTH );
  315.  
  316.       memset( g_status.line_buff+len, ' ', pad );
  317.       g_status.line_buff_len += pad;
  318.    }
  319.    /*
  320.     * if mode.inflate_tabs, let's don't entab the line until we get
  321.     *   thru processing this line, e.g. copying, numbering....
  322.     */
  323.    return( un_copy_line( ll, window, FALSE ) );
  324. }
  325.  
  326.  
  327. /*
  328.  * Name:    pad_dest_line
  329.  * Class:   helper function
  330.  * Purpose: To prepare a window/file for a block move or copy.
  331.  * Date:    June 5, 1991
  332.  * Passed:  window:  pointer to current window
  333.  *          dest_file: pointer to file information.
  334.  *          dest_line: pointer to line in file to prepare.
  335.  *          ll:        pointer to linked list node to insert new node
  336.  * Notes:   We are doing a BOX action (except DELETE).   We have come
  337.  *          to the end of the file and have no more lines.  All this
  338.  *          routine does is add a blank line to file.
  339.  */
  340. int  pad_dest_line( WINDOW *window, file_infos *dest_file, line_list_ptr ll )
  341. {
  342. int rc;
  343. text_ptr l;
  344. line_list_ptr new_node;
  345.  
  346.    rc = OK;
  347.  
  348.    l = NULL;
  349.    new_node = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  350.    if (rc == OK) {
  351.       new_node->len   = 0;
  352.       new_node->dirty = FALSE;
  353.       new_node->line  = l;
  354.       if (ll->next != NULL) {
  355.          ll->next->prev = new_node;
  356.          new_node->next = ll->next;
  357.          ll->next = new_node;
  358.          new_node->prev = ll;
  359.       } else {
  360.          new_node->next = ll;
  361.          if (ll->prev != NULL)
  362.             ll->prev->next = new_node;
  363.          new_node->prev = ll->prev;
  364.          ll->prev = new_node;
  365.          if (new_node->prev == NULL)
  366.             window->file_info->line_list = new_node;
  367.       }
  368.       ++dest_file->length;
  369.    } else {
  370.       /*
  371.        * file too big
  372.        */
  373.       error( WARNING, window->bottom_line, block4 );
  374.       if (new_node != NULL)
  375.          my_free( new_node );
  376.       rc = ERROR;
  377.    }
  378.    return( rc );
  379. }
  380.  
  381.  
  382. /*
  383.  * Name:    move_copy_delete_overlay_block
  384.  * Class:   primary editor function
  385.  * Purpose: Master BOX, STREAM, or LINE routine.
  386.  * Date:    June 5, 1991
  387.  * Passed:  window:  pointer to current window
  388.  * Notes:   Operations on BOXs, STREAMs, or LINEs require several common
  389.  *           operations.  All require finding the beginning and ending marks.
  390.  *          This routine will handle block operations across files.  Since one
  391.  *           must determine the relationship of source and destination blocks
  392.  *           within a file, it is relatively easy to expand this relationship
  393.  *           across files.
  394.  *          This is probably the most complicated routine in the editor.  It
  395.  *           is not easy to understand.
  396.  */
  397. int  move_copy_delete_overlay_block( WINDOW *window )
  398. {
  399. int  action;
  400. WINDOW *source_window;          /* source window for block moves */
  401. line_list_ptr source;           /* source for block moves */
  402. line_list_ptr dest;             /* destination for block moves */
  403. long number;                    /* number of characters for block moves */
  404. int  lens;                      /* length of source line */
  405. int  lend;                      /* length of destination line */
  406. int  add;                       /* characters being added from another line */
  407. int  block_len;                 /* length of the block */
  408. line_list_ptr block_start;      /* start of block in file */
  409. line_list_ptr block_end;  /* end of block in file - not same for LINE or BOX */
  410. int  prompt_line;
  411. int  same;                      /* are dest and source files the same file? */
  412. int  source_first;              /* is source file lower in memory than dest */
  413. file_infos *source_file;
  414. file_infos *dest_file;
  415. int  rcol, bc, ec;              /* temporary column variables */
  416. long rline;                     /* temporary real line variable */
  417. long br, er;                    /* temporary line variables */
  418. long block_num;                 /* starting number for block number */
  419. long block_inc;                 /* increment to use for block number */
  420. int  block_just;                /* left or right justify numbers? */
  421. int  block_type;
  422. int  fill_char;
  423. int  rc;
  424.  
  425.    /*
  426.     * initialize block variables
  427.     */
  428.    entab_linebuff( );
  429.    rc = un_copy_line( window->ll, window, TRUE );
  430.    if (g_status.marked == FALSE || rc == ERROR)
  431.       return( ERROR );
  432.    switch (g_status.command) {
  433.       case MoveBlock :
  434.          action = MOVE;
  435.          break;
  436.       case DeleteBlock :
  437.          action = DELETE;
  438.          break;
  439.       case CopyBlock :
  440.          action = COPY;
  441.          break;
  442.       case KopyBlock :
  443.          action = KOPY;
  444.          break;
  445.       case FillBlock :
  446.          action = FILL;
  447.          break;
  448.       case OverlayBlock :
  449.          action = OVERLAY;
  450.          break;
  451.       case NumberBlock :
  452.          action = NUMBER;
  453.          break;
  454.       case SwapBlock :
  455.          action = SWAP;
  456.          break;
  457.       default :
  458.          return( ERROR );
  459.    }
  460.    source_file = g_status.marked_file;
  461.    source_window = g_status.window_list;
  462.    for (; ptoul( source_window->file_info ) != ptoul( source_file );)
  463.       source_window = source_window->next;
  464.    prompt_line = window->bottom_line;
  465.    dest_file = window->file_info;
  466.    check_block( );
  467.    if (g_status.marked == FALSE)
  468.       return( ERROR );
  469.    block_start = source_file->block_start;
  470.    block_end = source_file->block_end;
  471.    if (block_start == NULL  ||  block_end == NULL)
  472.       return( ERROR );
  473.  
  474.    block_type = source_file->block_type;
  475.    if (block_type != LINE  &&  block_type != STREAM  &&  block_type != BOX)
  476.       return( ERROR );
  477.  
  478.    dest = window->ll;
  479.    rline = window->rline;
  480.    if (dest->len == EOF)
  481.       return( ERROR );
  482.    rc = OK;
  483.  
  484.    /*
  485.     * set up Beginning Column, Ending Column, Beginning Row, Ending Row
  486.     */
  487.    bc = source_file->block_bc;
  488.    ec = source_file->block_ec;
  489.    br = source_file->block_br;
  490.    er = source_file->block_er;
  491.  
  492.    /*
  493.     * if we are BOX FILLing or BOX NUMBERing, beginning column is bc,
  494.     *   not the column of cursor
  495.     */
  496.    rcol =  (action == FILL || action == NUMBER) ? bc : window->rcol;
  497.  
  498.    /*
  499.     * must find out if source and destination file are the same.
  500.     * it don't matter with FILL and DELETE - those actions only modify the
  501.     * source file.
  502.     */
  503.    source_first = same = FALSE;
  504.    if (action == FILL) {
  505.       if (block_type == BOX) {
  506.          if (get_block_fill_char( window, &fill_char ) == ERROR)
  507.             return( ERROR );
  508.          dest = block_start;
  509.          same = TRUE;
  510.       } else {
  511.          /*
  512.           * can only fill box blocks.
  513.           */
  514.          error( WARNING, prompt_line, block2 );
  515.          return( ERROR );
  516.       }
  517.    }
  518.    block_inc = 1;
  519.    if (action == NUMBER) {
  520.       if (block_type == BOX) {
  521.          if (get_block_numbers( window, &block_num, &block_inc, &block_just )
  522.               == ERROR)
  523.             return( ERROR );
  524.          dest = block_start;
  525.          same = TRUE;
  526.       } else {
  527.          /*
  528.           * can only number box blocks.
  529.           */
  530.          error( WARNING, prompt_line, block3a );
  531.          return( ERROR );
  532.       }
  533.    }
  534.    if (action == SWAP) {
  535.       if (block_type != BOX) {
  536.          /*
  537.           * can only swap box blocks.
  538.           */
  539.          error( WARNING, prompt_line, block3b );
  540.          return( ERROR );
  541.       }
  542.    }
  543.    if (source_file == dest_file && action != DELETE && action != FILL) {
  544.       same = TRUE;
  545.       if (block_type == BOX && action == MOVE) {
  546.          if (rline == br  &&  (rcol >= bc && rcol <= ec))
  547.             /*
  548.              * a block moved to within the block itself has no effect
  549.              */
  550.             return( ERROR );
  551.       } else if (block_type == LINE || block_type == STREAM) {
  552.          if (rline >= br && rline <= er) {
  553.             if (block_type == LINE) {
  554.                 /*
  555.                  * if COPYing or KOPYing within the block itself, reposition the
  556.                  * destination to the next line after the block (if it exists)
  557.                  */
  558.                if (action == COPY || action == KOPY)
  559.                   dest = block_end;
  560.                 /*
  561.                  * a block moved to within the block itself has no effect
  562.                  */
  563.                else if (action == MOVE)
  564.                   return( ERROR );
  565.             } else {
  566.  
  567.                /*
  568.                 * to find out if cursor is in a STREAM block we have to do
  569.                 * a few more tests.  if cursor is on the beginning row or
  570.                 * ending row, then check the beginning and ending column.
  571.                 */
  572.                if ((rline > br && rline < er) ||
  573.                    (br == er && rcol >= bc && rcol <= ec) ||
  574.                    (br != er && ((rline == br && rcol >= bc) ||
  575.                                  (rline == er && rcol <= ec)))) {
  576.  
  577.                   /*
  578.                    * if the cursor is in middle of STREAM, make destination
  579.                    * the last character following the STREAM block.
  580.                    */
  581.                   if (action == COPY || action == KOPY) {
  582.                      dest = block_end;
  583.                      rcol = ec + 1;
  584.                      rline = er;
  585.                   } else if (action == MOVE)
  586.                      return( ERROR );
  587.                }
  588.             }
  589.          }
  590.       }
  591.    }
  592.    if (br < rline)
  593.       source_first = TRUE;
  594.  
  595.    /*
  596.     * 1. can't create lines greater than g_display.line_length
  597.     * 2. if we are FILLing a BOX - fill block buff once right here
  598.     * 3. only allow overlaying BOXs
  599.     */
  600.    block_len = (ec+1) - bc;
  601.    if (block_type == BOX) {
  602.       if (action != DELETE && action != FILL) {
  603.          if (rcol + block_len > MAX_LINE_LENGTH) {
  604.             /*
  605.              * line too long
  606.              */
  607.             error( WARNING, prompt_line, ltol );
  608.             return( ERROR );
  609.          }
  610.       }
  611.    } else if (block_type == LINE) {
  612.       block_len = 0;
  613.       if (action == OVERLAY) {
  614.          /*
  615.           * can only overlay box blocks
  616.           */
  617.          error( WARNING, prompt_line, block5 );
  618.          return( ERROR );
  619.       }
  620.    } else if (block_type == STREAM) {
  621.  
  622.       if (action == OVERLAY) {
  623.          /*
  624.           * can only overlay box blocks
  625.           */
  626.          error( WARNING, prompt_line, block5 );
  627.          return( ERROR );
  628.       }
  629.  
  630.       lend = block_end->len;
  631.       if (action == DELETE || action == MOVE) {
  632.  
  633.          /*
  634.           * Is what's left on start of STREAM block line plus what's left at
  635.           * end of STREAM block line too long?
  636.           */
  637.          if (lend > ec)
  638.             lend -= ec;
  639.          else
  640.             lend = 0;
  641.          if (bc + lend > MAX_LINE_LENGTH) {
  642.             /*
  643.              * line too long
  644.              */
  645.             error( WARNING, prompt_line, ltol );
  646.             return( ERROR );
  647.          }
  648.       }
  649.  
  650.       if (action != DELETE) {
  651.  
  652.          /*
  653.           * We are doing a MOVE, COPY, or KOPY.  Find out if what's on the
  654.           * current line plus the start of the STREAM line are too long.
  655.           * Then find out if end of the STREAM line plus what's left of
  656.           * the current line are too long.
  657.           */
  658.          lens = block_start->len;
  659.  
  660.          /*
  661.           * if we had to move the destination of the STREAM COPY or KOPY
  662.           * to the end of the STREAM block, then dest and window->ll->line
  663.           * will not be the same.  In this case, set length to length of
  664.           * first line in STREAM block.  Then we can add the residue of
  665.           * the first line in block plus residue of the last line of block.
  666.           */
  667.          if (dest->line == window->ll->line)
  668.             add = dest->len;
  669.          else
  670.             add = lens;
  671.  
  672.          /*
  673.           * Is current line plus start of STREAM block line too long?
  674.           */
  675.          if (lens > bc)
  676.             lens -= bc;
  677.          else
  678.             lens = 0;
  679.          if (rcol + lens > MAX_LINE_LENGTH) {
  680.             /*
  681.              * line too long
  682.              */
  683.             error( WARNING, prompt_line, ltol );
  684.             return( ERROR );
  685.          }
  686.  
  687.          /*
  688.           * Is residue of current line plus residue of STREAM block line
  689.           * too long?
  690.           */
  691.          if (add > bc)
  692.             add -= bc;
  693.          else
  694.             add = 0;
  695.          if (lend > ec)
  696.             lend -= ec;
  697.          else
  698.             lend = 0;
  699.          if (add + lend > MAX_LINE_LENGTH) {
  700.             /*
  701.              * line too long
  702.              */
  703.             error( WARNING, prompt_line, ltol );
  704.             return( ERROR );
  705.          }
  706.       }
  707.       if (ptoul( block_start ) == ptoul( block_end )) {
  708.          block_type = BOX;
  709.          block_len = (ec+1) - bc;
  710.       }
  711.    }
  712.  
  713.    if (mode.do_backups == TRUE) {
  714.       switch (action) {
  715.          case MOVE :
  716.          case DELETE :
  717.          case COPY :
  718.          case KOPY :
  719.          case SWAP :
  720.             window->file_info->modified = TRUE;
  721.             rc = backup_file( window );
  722.             break;
  723.       }
  724.       switch (action) {
  725.          case MOVE :
  726.          case DELETE :
  727.          case FILL :
  728.          case NUMBER :
  729.          case SWAP :
  730.             source_window->file_info->modified = TRUE;
  731.             if (rc != ERROR)
  732.                rc = backup_file( source_window );
  733.             break;
  734.       }
  735.    }
  736.    source = block_start;
  737.  
  738.    assert( block_start != NULL );
  739.    assert( block_start->len != EOF );
  740.    assert( block_end != NULL );
  741.    assert( block_end->len != EOF );
  742.  
  743.    if (block_type == LINE)
  744.       do_line_block( window,  source_window,  action,
  745.                      source_file,  dest_file,  block_start,  block_end,
  746.                      source,  dest,  br,  er,  &rc );
  747.  
  748.    else if (block_type == STREAM)
  749.       do_stream_block( window,  source_window,  action,
  750.                        source_file,  dest_file,  block_start,  block_end,
  751.                        source,  dest,  rline,  br,  er,  bc,  ec,  rcol,  &rc );
  752.  
  753.    else
  754.       do_box_block( window,  source_window,  action,
  755.                     source_file,  dest_file,  source,  dest,  br,  er,
  756.                     block_inc, rline, block_num, block_just, fill_char,
  757.                     same, block_len, bc, ec,  rcol, &rc );
  758.  
  759.    dest_file->modified = TRUE;
  760.    dest_file->dirty = GLOBAL;
  761.    if (action == MOVE || action == DELETE || action == FILL || action==NUMBER) {
  762.       source_file->modified = TRUE;
  763.       source_file->dirty = GLOBAL;
  764.    }
  765.  
  766.    /*
  767.     * unless we are doing a KOPY, FILL, NUMBER, or OVERLAY we need to unmark
  768.     * the block.  if we just did a KOPY, the beginning and ending may have
  769.     * changed.  so, we must readjust beginning and ending rows.
  770.     */
  771.    if (action == KOPY) {
  772.       if (same && !source_first && block_type == LINE  &&  rc != ERROR) {
  773.          number = (er+1) - br;
  774.          source_file->block_br += number;
  775.          source_file->block_er += number;
  776.       }
  777.    } else if (action != FILL && action != OVERLAY && action != NUMBER)
  778.       unmark_block( window );
  779.    show_avail_mem( );
  780.    g_status.copied = FALSE;
  781.    return( rc );
  782. }
  783.  
  784.  
  785. /*
  786.  * Name:    do_line_block
  787.  * Purpose: delete, move, copy, or kopy a LINE block
  788.  * Date:    April 1, 1993
  789.  * Passed:  window:  pointer to current window
  790.  * Passed:  window:         pointer to destination window (current window)
  791.  *          source_window:  pointer to source window
  792.  *          action:         block action  --  KOPY, MOVE, etc...
  793.  *          source_file:    pointer to source file structure
  794.  *          dest_file:      pointer to destination file
  795.  *          block_start:    pointer to first node in block
  796.  *          block_end:      pointer to last node in block
  797.  *          source:         pointer to source node
  798.  *          dest:           pointer to destination node
  799.  *          br:             beginning line number in marked block
  800.  *          er:             ending line number in marked block
  801.  *          rc:             return code
  802.  */
  803. void do_line_block( WINDOW *window,  WINDOW *source_window,  int action,
  804.                     file_infos *source_file,  file_infos *dest_file,
  805.                     line_list_ptr block_start,  line_list_ptr block_end,
  806.                     line_list_ptr source,  line_list_ptr dest,
  807.                     long br,  long er, int *rc )
  808. {
  809. line_list_ptr temp_ll;          /* temporary list pointer */
  810. text_ptr l;
  811. int  lens;                      /* length of source line */
  812. long li;                        /* temporary line variables */
  813. long diff;
  814.  
  815.    if (action == COPY || action == KOPY) {
  816.  
  817.       assert( br >= 1 );
  818.       assert( br <= source_file->length );
  819.       assert( er >= br );
  820.       assert( er <= source_file->length );
  821.  
  822.       for (li=br; li <= er  &&  *rc == OK; li++) {
  823.          lens = source->len;
  824.  
  825.          assert( lens * sizeof(char) < MAX_LINE_LENGTH );
  826.  
  827.          l = (text_ptr)my_malloc( lens * sizeof(char), rc );
  828.          temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc );
  829.          if (*rc == OK) {
  830.             if (lens > 0)
  831.                _fmemcpy( l, source->line, lens );
  832.             temp_ll->line  = l;
  833.             temp_ll->len   = lens;
  834.             temp_ll->dirty = TRUE;
  835.  
  836.             if (dest->next != NULL) {
  837.                dest->next->prev = temp_ll;
  838.                temp_ll->next = dest->next;
  839.                dest->next = temp_ll;
  840.                temp_ll->prev = dest;
  841.             } else {
  842.                temp_ll->next = dest;
  843.                if (dest->prev != NULL)
  844.                   dest->prev->next = temp_ll;
  845.                temp_ll->prev = dest->prev;
  846.                dest->prev = temp_ll;
  847.                if (temp_ll->prev == NULL)
  848.                   window->file_info->line_list = temp_ll;
  849.             }
  850.  
  851.             dest = temp_ll;
  852.             source = source->next;
  853.          } else {
  854.             /*
  855.              * file too big
  856.              */
  857.             error( WARNING, window->bottom_line, dir3 );
  858.             if (l != NULL)
  859.                my_free( l );
  860.             if (temp_ll != NULL)
  861.                my_free( temp_ll );
  862.             *rc = ERROR;
  863.             er = li - 1;
  864.          }
  865.       }
  866.    } else if (action == MOVE) {
  867.       if (dest->len != EOF  &&  dest->next != NULL) {
  868.          temp_ll = block_start;
  869.          for (li=br; li <= er  &&  *rc == OK; li++) {
  870.             temp_ll->dirty = TRUE;
  871.             temp_ll = temp_ll->next;
  872.          }
  873.          if (block_start->prev == NULL)
  874.             source_file->line_list = block_end->next;
  875.          if (block_start->prev != NULL)
  876.             block_start->prev->next = block_end->next;
  877.          block_end->next->prev = block_start->prev;
  878.          dest->next->prev = block_end;
  879.          block_start->prev = dest;
  880.          block_end->next = dest->next;
  881.          dest->next = block_start;
  882.       }
  883.    } else if (action == DELETE) {
  884.       block_end->next->prev = block_start->prev;
  885.       if (block_start->prev == NULL)
  886.          source_file->line_list = block_end->next;
  887.       else
  888.          block_start->prev->next = block_end->next;
  889.       block_end->next = NULL;
  890.       while (block_start != NULL) {
  891.          temp_ll = block_start;
  892.          block_start = block_start->next;
  893.          if (temp_ll->line != NULL)
  894.             my_free( temp_ll->line );
  895.          my_free( temp_ll );
  896.       }
  897.    }
  898.  
  899.    diff =  er + 1L - br;
  900.    if (action == COPY || action == KOPY || action == MOVE)
  901.       dest_file->length += diff;
  902.    if (action == DELETE || action == MOVE)
  903.       source_file->length -= diff;
  904.    if (action == DELETE && source_window->rline >= br) {
  905.       source_window->rline -= diff;
  906.       if (source_window->rline < br)
  907.          source_window->rline = br;
  908.    }
  909.    /*
  910.     * restore all cursors in all windows
  911.     */
  912.    restore_cursors( dest_file );
  913.    if (dest_file != source_file)
  914.       restore_cursors( source_file );
  915.    show_avail_mem( );
  916. }
  917.  
  918.  
  919. /*
  920.  * Name:    do_stream_block
  921.  * Purpose: delete, move, copy, or kopy a STREAM block
  922.  * Date:    June 5, 1991
  923.  * Passed:  window:         pointer to destination window (current window)
  924.  *          source_window:  pointer to source window
  925.  *          action:         block action  --  KOPY, MOVE, etc...
  926.  *          source_file:    pointer to source file structure
  927.  *          dest_file:      pointer to destination file
  928.  *          block_start:    pointer to first node in block
  929.  *          block_end:      pointer to last node in block
  930.  *          source:         pointer to source node
  931.  *          dest:           pointer to destination node
  932.  *          rline:          current line number in destination file
  933.  *          br:             beginning line number in marked block
  934.  *          er:             ending line number in marked block
  935.  *          bc:             beginning column of block
  936.  *          ec:             ending column of block
  937.  *          rcol:           current column of cursor
  938.  *          rc:             return code
  939.  */
  940. void do_stream_block( WINDOW *window,  WINDOW *source_window,  int action,
  941.                     file_infos *source_file,  file_infos *dest_file,
  942.                     line_list_ptr block_start,  line_list_ptr block_end,
  943.                     line_list_ptr source,  line_list_ptr dest, long rline,
  944.                     long br,  long er, int bc, int ec, int rcol, int *rc )
  945. {
  946. line_list_ptr temp_ll;          /* temporary list pointer */
  947. text_ptr l;
  948. int  lens;                      /* length of source line */
  949. int  lend;                      /* length of destination line */
  950. long li;                        /* temporary line variables */
  951. long diff;
  952. WINDOW s_w, d_w;                /* a couple of temporary WINDOWs */
  953.  
  954.    dup_window_info( &s_w, source_window );
  955.    dup_window_info( &d_w, window );
  956.    s_w.rline   = br;
  957.    s_w.ll      = block_start;
  958.    s_w.visible = FALSE;
  959.    d_w.rline   = rline;
  960.    d_w.ll      = dest;
  961.    d_w.visible = FALSE;
  962.  
  963.    /*
  964.     * pad the start of the STREAM block if needed.
  965.     */
  966.    lens = block_start->len;
  967.    detab_a_line( block_start->line, &lens );
  968.    if (lens < bc || mode.inflate_tabs)
  969.       *rc = prepare_block( &s_w, block_start, bc );
  970.  
  971.    /*
  972.     * pad the end of the STREAM block if needed.
  973.     */
  974.    lens = block_end->len;
  975.    detab_a_line( block_end->line, &lens );
  976.    if (*rc == OK  &&  (lens < ec+1  ||  mode.inflate_tabs))
  977.       *rc = prepare_block( &s_w, block_end, ec+1 );
  978.  
  979.    /*
  980.     * pad the destination line if necessary
  981.     */
  982.    copy_line( dest );
  983.    detab_linebuff( );
  984.    *rc = un_copy_line( dest, &d_w, FALSE );
  985.    lend = dest->len;
  986.    if (*rc == OK && (action==MOVE || action==COPY || action==KOPY)) {
  987.       if (lend < rcol || mode.inflate_tabs)
  988.          *rc = prepare_block( &d_w, dest, rcol );
  989.    }
  990.  
  991.    if ((action == COPY || action == KOPY) && *rc == OK) {
  992.  
  993.       /*
  994.        * concatenate the end of the STREAM block with the end of the
  995.        *   destination line.
  996.        */
  997.       lens = dest->len - rcol;
  998.  
  999.       assert( lens >= 0 );
  1000.       assert( lens <= MAX_LINE_LENGTH );
  1001.       assert( ec + 1 >= 0 );
  1002.       assert( ec + 1 <= MAX_LINE_LENGTH );
  1003.       assert( rcol >= 0 );
  1004.  
  1005.       _fmemcpy( g_status.line_buff, block_end->line, ec+1 );
  1006.       _fmemcpy( g_status.line_buff+ec+1, dest->line+rcol, lens );
  1007.       lens += ec + 1;
  1008.       g_status.line_buff_len = lens;
  1009.  
  1010.       temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc );
  1011.       if (*rc == OK) {
  1012.          temp_ll->line  = NULL;
  1013.          temp_ll->len   = 0;
  1014.          temp_ll->dirty = FALSE;
  1015.          g_status.copied = TRUE;
  1016.          *rc = un_copy_line( temp_ll, &d_w, TRUE );
  1017.  
  1018.          if (*rc == OK) {
  1019.             dest->next->prev = temp_ll;
  1020.             temp_ll->next = dest->next;
  1021.             dest->next = temp_ll;
  1022.             temp_ll->prev = dest;
  1023.          } else
  1024.             if (temp_ll != NULL)
  1025.                my_free( temp_ll );
  1026.       } else {
  1027.          if (temp_ll != NULL)
  1028.             my_free( temp_ll );
  1029.       }
  1030.  
  1031.       /*
  1032.        * file too big
  1033.        */
  1034.       if (*rc != OK)
  1035.          error( WARNING, window->bottom_line, dir3 );
  1036.  
  1037.       if (*rc == OK) {
  1038.          g_status.copied = FALSE;
  1039.          copy_line( dest );
  1040.          lens = block_start->len - bc;
  1041.  
  1042.          assert( lens >= 0 );
  1043.          assert( lens <= MAX_LINE_LENGTH );
  1044.          assert( bc >= 0 );
  1045.          assert( bc <= MAX_LINE_LENGTH );
  1046.          assert( rcol >= 0 );
  1047.  
  1048.          _fmemcpy( g_status.line_buff+rcol, block_start->line+bc, lens );
  1049.          lens = rcol + lens;
  1050.          g_status.line_buff_len = lens;
  1051.          *rc = un_copy_line( dest, &d_w, TRUE );
  1052.       }
  1053.  
  1054.       source = block_start->next;
  1055.       for (li=br+1; li < er  &&  *rc == OK; li++) {
  1056.          lens = source->len;
  1057.          temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc );
  1058.  
  1059.          assert( lens >= 0 );
  1060.          assert( lens <= MAX_LINE_LENGTH );
  1061.  
  1062.          l = (text_ptr)my_malloc( lens * sizeof(char), rc );
  1063.          if (*rc == OK) {
  1064.             if (lens > 0)
  1065.                _fmemcpy( l, source->line, lens );
  1066.             temp_ll->line  = l;
  1067.             temp_ll->len   = lens;
  1068.             temp_ll->dirty = TRUE;
  1069.  
  1070.             if (dest->next != NULL) {
  1071.                dest->next->prev = temp_ll;
  1072.                temp_ll->next = dest->next;
  1073.                dest->next = temp_ll;
  1074.                temp_ll->prev = dest;
  1075.             } else {
  1076.                temp_ll->next = dest;
  1077.                if (dest->prev != NULL)
  1078.                   dest->prev->next = temp_ll;
  1079.                temp_ll->prev = dest->prev;
  1080.                dest->prev = temp_ll;
  1081.                if (temp_ll->prev == NULL)
  1082.                   window->file_info->line_list = temp_ll;
  1083.             }
  1084.  
  1085.             dest = temp_ll;
  1086.             source = source->next;
  1087.          } else {
  1088.             /*
  1089.              * file too big
  1090.              */
  1091.             error( WARNING, window->bottom_line, dir3 );
  1092.             if (l != NULL)
  1093.                my_free( l );
  1094.             if (temp_ll != NULL)
  1095.                my_free( temp_ll );
  1096.             *rc = WARNING;
  1097.          }
  1098.       }
  1099.    } else if (action == MOVE) {
  1100.  
  1101.       /*
  1102.        * is the dest on the same line as the block_start?
  1103.        */
  1104.       if (ptoul( dest ) == ptoul( block_start )) {
  1105.  
  1106.          /*
  1107.           * move the text between rcol and bc in block_start->line
  1108.           *   to block_end->line + ec.
  1109.           */
  1110.          lens = bc - rcol;
  1111.          lend = block_end->len - (ec + 1);
  1112.          g_status.copied = FALSE;
  1113.          copy_line( block_end );
  1114.  
  1115.  
  1116.          assert( lens >= 0 );
  1117.          assert( lens <= MAX_LINE_LENGTH );
  1118.          assert( lend >= 0 );
  1119.          assert( lend <= MAX_LINE_LENGTH );
  1120.          assert( ec + lens + 1 <= MAX_LINE_LENGTH );
  1121.          assert( rcol >= 0 );
  1122.  
  1123.  
  1124.          _fmemmove( g_status.line_buff + ec + lens + 1,
  1125.                     g_status.line_buff + ec + 1,  lend );
  1126.          _fmemcpy( g_status.line_buff+ec+1, block_start->line+rcol, lens );
  1127.          g_status.line_buff_len = block_end->len + lens;
  1128.          *rc = un_copy_line( block_end, &d_w, TRUE );
  1129.  
  1130.          /*
  1131.           * now, remove the text between rcol and bc on block_start->line
  1132.           */
  1133.          if (*rc == OK) {
  1134.             lend = block_start->len - bc;
  1135.             copy_line( block_start );
  1136.  
  1137.             assert( lend >= 0 );
  1138.             assert( lend < MAX_LINE_LENGTH );
  1139.  
  1140.             _fmemmove( g_status.line_buff + rcol,
  1141.                        g_status.line_buff + bc, lend );
  1142.  
  1143.             assert( block_start->len - (bc - rcol) >= 0 );
  1144.             assert( block_start->len - (bc - rcol) <= MAX_LINE_LENGTH );
  1145.  
  1146.             g_status.line_buff_len = block_start->len - (bc - rcol);
  1147.             *rc = un_copy_line( block_start, &d_w, TRUE );
  1148.          }
  1149.  
  1150.       /*
  1151.        * is the dest on the same line as the block_end?
  1152.        */
  1153.       } else if (ptoul( dest ) == ptoul( block_end )) {
  1154.  
  1155.          /*
  1156.           * move the text between rcol and ec on block_end->line to
  1157.           *   block_start->line + bc.
  1158.           */
  1159.          lens = rcol - ec;
  1160.          lend = block_start->len - bc;
  1161.          g_status.copied = FALSE;
  1162.          copy_line( block_start );
  1163.  
  1164.          assert( lens >= 0 );
  1165.          assert( lens <= MAX_LINE_LENGTH );
  1166.          assert( lend >= 0 );
  1167.          assert( lend <= MAX_LINE_LENGTH );
  1168.          assert( bc + lens <= MAX_LINE_LENGTH );
  1169.          assert( ec + 1 >= 0 );
  1170.  
  1171.          _fmemmove( g_status.line_buff + bc + lens,
  1172.                     g_status.line_buff + bc,  lend );
  1173.          _fmemcpy( g_status.line_buff+bc, block_end->line+ec+1, lens );
  1174.  
  1175.          assert( block_start->len + lens >= 0 );
  1176.          assert( block_start->len + lens <= MAX_LINE_LENGTH );
  1177.  
  1178.          g_status.line_buff_len = block_start->len + lens;
  1179.          *rc = un_copy_line( block_start, &d_w, TRUE );
  1180.  
  1181.          /*
  1182.           * now, remove the text on block_end->line between rcol and ec
  1183.           */
  1184.          if (*rc == OK) {
  1185.             lend = block_end->len - (rcol + 1);
  1186.             copy_line( block_end );
  1187.  
  1188.             assert( lend >= 0 );
  1189.             assert( lend <= MAX_LINE_LENGTH );
  1190.             assert( ec + 1 >= 0 );
  1191.             assert( rcol + 1 >= 0 );
  1192.             assert( ec + 1 <= MAX_LINE_LENGTH );
  1193.             assert( rcol + 1 <= MAX_LINE_LENGTH );
  1194.             assert( block_end->len - (rcol - ec) >= 0 );
  1195.             assert( block_end->len - (rcol - ec) <= MAX_LINE_LENGTH );
  1196.  
  1197.  
  1198.             _fmemmove( g_status.line_buff + ec + 1,
  1199.                        g_status.line_buff + rcol + 1, lend );
  1200.             g_status.line_buff_len = block_end->len - (rcol - ec);
  1201.             *rc = un_copy_line( block_end, &d_w, TRUE );
  1202.          }
  1203.       } else {
  1204.  
  1205.          lens = dest->len - rcol;
  1206.  
  1207.          assert( ec + 1 >= 0 );
  1208.          assert( ec + 1 <= MAX_LINE_LENGTH );
  1209.          assert( lens >= 0 );
  1210.          assert( lens <= MAX_LINE_LENGTH );
  1211.          assert( rcol >= 0 );
  1212.          assert( rcol <= MAX_LINE_LENGTH );
  1213.  
  1214.          _fmemcpy( g_status.line_buff, block_end->line, ec+1 );
  1215.          _fmemcpy( g_status.line_buff+ec+1, dest->line+rcol, lens );
  1216.          lens += ec + 1;
  1217.          g_status.line_buff_len = lens;
  1218.  
  1219.          temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc );
  1220.          if (*rc == OK) {
  1221.             temp_ll->line  = NULL;
  1222.             temp_ll->len   = 0;
  1223.             temp_ll->dirty = FALSE;
  1224.             g_status.copied = TRUE;
  1225.             *rc = un_copy_line( temp_ll, &d_w, TRUE );
  1226.  
  1227.             if (*rc != ERROR) {
  1228.                dest->next->prev = temp_ll;
  1229.                temp_ll->next = dest->next;
  1230.                dest->next = temp_ll;
  1231.                temp_ll->prev = dest;
  1232.             } else
  1233.                if (temp_ll != NULL)
  1234.                   my_free( temp_ll );
  1235.          } else {
  1236.             if (temp_ll != NULL)
  1237.                my_free( temp_ll );
  1238.          }
  1239.  
  1240.          /*
  1241.           * file too big
  1242.           */
  1243.          if (*rc != OK)
  1244.             error( WARNING, window->bottom_line, dir3 );
  1245.  
  1246.          if (*rc == OK) {
  1247.             copy_line( dest );
  1248.             lens = block_start->len - bc;
  1249.  
  1250.             assert( bc >= 0 );
  1251.             assert( bc <= MAX_LINE_LENGTH );
  1252.             assert( lens >= 0 );
  1253.             assert( lens <= MAX_LINE_LENGTH );
  1254.             assert( rcol >= 0 );
  1255.             assert( rcol <= MAX_LINE_LENGTH );
  1256.  
  1257.             _fmemcpy( g_status.line_buff+rcol, block_start->line+bc, lens );
  1258.             g_status.line_buff_len = lens + rcol;
  1259.             *rc = un_copy_line( dest, &d_w, TRUE );
  1260.             dest->dirty = TRUE;
  1261.          }
  1262.  
  1263.          if (*rc == OK  &&  ptoul( block_start->next ) != ptoul( block_end )) {
  1264.             block_start->next->prev = dest;
  1265.             temp_ll->prev = block_end->prev;
  1266.             block_end->prev->next = temp_ll;
  1267.             dest->next = block_start->next;
  1268.          }
  1269.  
  1270.          if (*rc == OK) {
  1271.             copy_line( block_start );
  1272.             detab_linebuff( );
  1273.             lend = bc;
  1274.             lens = block_end->len - (ec + 1);
  1275.  
  1276.             assert( bc >= 0 );
  1277.             assert( bc <= MAX_LINE_LENGTH );
  1278.             assert( lens >= 0 );
  1279.             assert( lens <= MAX_LINE_LENGTH );
  1280.             assert( lend >= 0 );
  1281.             assert( lend <= MAX_LINE_LENGTH );
  1282.             assert( ec + 1 >= 0 );
  1283.             assert( ec + 1 <= MAX_LINE_LENGTH );
  1284.             assert( lens + lend >= 0 );
  1285.             assert( lens + lend <= MAX_LINE_LENGTH );
  1286.  
  1287.             _fmemcpy( g_status.line_buff+bc, block_end->line+ec+1, lens );
  1288.             g_status.line_buff_len = lend + lens;
  1289.             *rc = un_copy_line( block_start, &s_w, TRUE );
  1290.             block_start->dirty = TRUE;
  1291.             block_start->next = block_end->next;
  1292.             block_end->next->prev = block_start;
  1293.             if (block_end->line != NULL)
  1294.                my_free( block_end->line );
  1295.             my_free( block_end );
  1296.          }
  1297.       }
  1298.    } else if (action == DELETE) {
  1299.       copy_line( block_start );
  1300.       lens = block_end->len - (ec + 1);
  1301.  
  1302.       assert( bc >= 0 );
  1303.       assert( bc <= MAX_LINE_LENGTH );
  1304.       assert( lens >= 0 );
  1305.       assert( lens <= MAX_LINE_LENGTH );
  1306.       assert( ec + 1 >= 0 );
  1307.       assert( ec + 1 <= MAX_LINE_LENGTH );
  1308.       assert( bc + lens >= 0 );
  1309.       assert( bc + lens <= MAX_LINE_LENGTH );
  1310.  
  1311.       _fmemcpy( g_status.line_buff+bc, block_end->line + ec+1, lens );
  1312.       g_status.line_buff_len = bc + lens;
  1313.       *rc = un_copy_line( block_start, &s_w, TRUE );
  1314.       block_start->dirty = TRUE;
  1315.       source = block_start->next;
  1316.       block_start->next = block_end->next;
  1317.       block_end->next->prev = block_start;
  1318.       block_end->next = NULL;
  1319.       while (source != NULL) {
  1320.          temp_ll = source;
  1321.          source = source->next;
  1322.          if (temp_ll->line != NULL)
  1323.             my_free( temp_ll->line );
  1324.          my_free( temp_ll );
  1325.       }
  1326.    }
  1327.  
  1328.    if (*rc == OK) {
  1329.       diff = er - br;
  1330.       if (action == COPY || action == KOPY || action == MOVE)
  1331.          dest_file->length += diff;
  1332.       if (action == DELETE || action == MOVE)
  1333.          source_file->length -= diff;
  1334.       if (action == DELETE && source_window->rline >= br) {
  1335.          source_window->rline -= diff;
  1336.          if (source_window->rline < br)
  1337.             source_window->rline = br;
  1338.       }
  1339.    }
  1340.  
  1341.    /*
  1342.     * restore all cursors in all windows
  1343.     */
  1344.    restore_cursors( dest_file );
  1345.    if (dest_file != source_file)
  1346.       restore_cursors( source_file );
  1347.    show_avail_mem( );
  1348. }
  1349.  
  1350.  
  1351. /*
  1352.  * Name:    do_box_block
  1353.  * Purpose: delete, move, copy, or kopy a BOX block
  1354.  * Date:    June 5, 1991
  1355.  * Passed:  window:         pointer to destination window (current window)
  1356.  *          source_window:  pointer to source window
  1357.  *          action:         block action  --  OVERLAY, FILL, etc...
  1358.  *          source_file:    pointer to source file structure
  1359.  *          dest_file:      pointer to destination file
  1360.  *          source:         pointer to source node
  1361.  *          dest:           pointer to destination node
  1362.  *          br:             beginning line number in marked block
  1363.  *          er:             ending line number in marked block
  1364.  *          block_inc:      increment used to number a block
  1365.  *          rline:          current line number in destination file
  1366.  *          block_num:      starting number when numbering a block
  1367.  *          block_just:     LEFT or RIGHT justified numbers in block
  1368.  *          fill_char:      character to fill a block
  1369.  *          same:           are source and destination files same? T or F
  1370.  *          block_len:      width of box block
  1371.  *          bc:             beginning column of block
  1372.  *          ec:             ending column of block
  1373.  *          rcol:           current column of cursor
  1374.  *          rc:             return code
  1375.  */
  1376. void do_box_block( WINDOW *window,  WINDOW *source_window,  int action,
  1377.                     file_infos *source_file,  file_infos *dest_file,
  1378.                     line_list_ptr source,  line_list_ptr dest, long br,
  1379.                     long er, long block_inc,
  1380.                     long rline, long block_num, int block_just, int fill_char,
  1381.                     int same, int block_len, int bc, int ec, int rcol, int *rc )
  1382. {
  1383. line_list_ptr p;                /* temporary list pointer */
  1384. int  lens;                      /* length of source line */
  1385. int  lend;                      /* length of destination line */
  1386. int  add;                       /* characters being added from another line */
  1387. char *block_buff;
  1388. char *swap_buff;
  1389. int  xbc, xec;                  /* temporary column variables */
  1390. long li;                        /* temporary line variables */
  1391. long dest_add;                  /* number of bytes added to destination file */
  1392. WINDOW s_w, d_w;       /* a couple of temporary WINDOWs for BOX stuff */
  1393. int  padded_file;
  1394. WINDOW *w;
  1395.  
  1396.    padded_file = FALSE;
  1397.    dup_window_info( &s_w, source_window );
  1398.    dup_window_info( &d_w, window );
  1399.    s_w.rline   = br;
  1400.    s_w.ll      = source;
  1401.    s_w.visible = FALSE;
  1402.    d_w.rline   = rline;
  1403.    d_w.ll      = dest;
  1404.    d_w.visible = FALSE;
  1405.  
  1406.    block_buff = (char *)calloc( BUFF_SIZE + 2, sizeof(char) );
  1407.    swap_buff  = (char *)calloc( BUFF_SIZE + 2, sizeof(char) );
  1408.    if (block_buff == NULL || swap_buff == NULL) {
  1409.       error( WARNING, window->bottom_line, block4 );
  1410.       *rc = ERROR;
  1411.    }
  1412.  
  1413.    /*
  1414.     * special case for block actions.  since block actions always
  1415.     *   move forward thru the file, overlapping text in an OVERLAY
  1416.     *   action don't do right.  make the operation start at the end
  1417.     *   of the block and work backwards.
  1418.     */
  1419.    if (*rc == OK  &&  (action == OVERLAY || action == SWAP) &&
  1420.            same  &&  rline > br  &&  rline <= er) {
  1421.  
  1422.       /*
  1423.        * see if we need to add padd lines at eof.
  1424.        */
  1425.       dest_add = rline - br;
  1426.       if (dest_add + er > window->file_info->length) {
  1427.          dest_add = dest_add - (window->file_info->length - er);
  1428.          p = dest_file->line_list_end->prev;
  1429.          for (; dest_add > 0  &&  *rc == OK; dest_add--)
  1430.             *rc = pad_dest_line( window, dest_file, p );
  1431.          padded_file = TRUE;
  1432.       }
  1433.  
  1434.       /*
  1435.        * move source and dest pointers to the end of the OVERLAY
  1436.        */
  1437.       for (li=er-br; li > 0; li--) {
  1438.          load_undo_buffer( dest_file, dest->line, dest->len );
  1439.          dest = dest->next;
  1440.          ++d_w.rline;
  1441.          source = source->next;
  1442.          ++s_w.rline;
  1443.       }
  1444.  
  1445.       /*
  1446.        * work backwards so the overlapped OVERLAY block don't use
  1447.        * overlayed text to fill the block.  same for SWAPPing blocks.
  1448.        */
  1449.       for (li=er; *rc == OK  &&  li >= br  &&  !g_status.control_break;
  1450.                                        li--, s_w.rline--, d_w.rline--) {
  1451.          lens = find_end( source->line, source->len );
  1452.          lend = find_end( dest->line, dest->len );
  1453.          if (lens != 0 || lend != 0) {
  1454.             load_box_buff( block_buff, source, bc, ec, ' ' );
  1455.             if (action == SWAP)
  1456.                load_box_buff( swap_buff, dest, rcol, rcol+block_len, ' ' );
  1457.             *rc = copy_buff_2file( &d_w, block_buff, dest, rcol,
  1458.                                     block_len, action );
  1459.             dest->dirty = TRUE;
  1460.             if (action == SWAP) {
  1461.                add = 0;
  1462.                *rc = copy_buff_2file( &s_w, swap_buff, source, bc,
  1463.                                 block_len, action );
  1464.                source->dirty = TRUE;
  1465.             }
  1466.          }
  1467.          source = source->prev;
  1468.          dest = dest->prev;
  1469.       }
  1470.    } else {
  1471.       if (action == FILL)
  1472.          block_fill( block_buff, fill_char, block_len );
  1473.       for (li=br; *rc == OK  &&  li <= er  &&  !g_status.control_break;
  1474.                            li++, s_w.rline++, d_w.rline++) {
  1475.          lens = find_end( source->line, source->len );
  1476.          lend = find_end( dest->line, dest->len );
  1477.  
  1478.          switch (action) {
  1479.             case FILL    :
  1480.             case NUMBER  :
  1481.             case DELETE  :
  1482.             case MOVE    :
  1483.                load_undo_buffer( source_file, source->line, source->len );
  1484.                break;
  1485.             case COPY    :
  1486.             case KOPY    :
  1487.             case OVERLAY :
  1488.                load_undo_buffer( dest_file, dest->line, dest->len );
  1489.                break;
  1490.          }
  1491.  
  1492.          /*
  1493.           * with FILL and NUMBER operations, we're just adding chars
  1494.           *   to the file at the source location.  we don't have to
  1495.           *   worry about bookkeeping.
  1496.           */
  1497.          if (action == FILL || action == NUMBER) {
  1498.             if (action == NUMBER) {
  1499.               number_block_buff( block_buff, block_len, block_num, block_just );
  1500.               block_num += block_inc;
  1501.             }
  1502.             *rc = copy_buff_2file( &s_w, block_buff, source, rcol,
  1503.                                 block_len, action );
  1504.             source->dirty = TRUE;
  1505.  
  1506.          /*
  1507.           * if we are doing a BOX action and both the source and
  1508.           * destination are 0 then we have nothing to do.
  1509.           */
  1510.          } else if (lens != 0 || lend != 0) {
  1511.  
  1512.             /*
  1513.              * do actions that may require adding to file
  1514.              */
  1515.             if (action == MOVE     ||  action == COPY || action == KOPY ||
  1516.                 action == OVERLAY  ||  action == SWAP) {
  1517.                xbc = bc;
  1518.                xec = ec;
  1519.                if (action != OVERLAY  &&  action != SWAP  &&  same) {
  1520.                   if (rcol < bc && rline > br && rline <=er)
  1521.                      if (li >= rline) {
  1522.                         xbc = bc + block_len;
  1523.                         xec = ec + block_len;
  1524.                      }
  1525.                }
  1526.                load_box_buff( block_buff, source, xbc, xec, ' ' );
  1527.                if (action == SWAP)
  1528.                   load_box_buff( swap_buff, dest, rcol, rcol+block_len, ' ' );
  1529.                *rc = copy_buff_2file( &d_w, block_buff, dest, rcol,
  1530.                                 block_len, action );
  1531.                dest->dirty = TRUE;
  1532.                if (action == SWAP && *rc == OK) {
  1533.                   *rc = copy_buff_2file( &s_w, swap_buff, source, xbc,
  1534.                                    block_len, action );
  1535.                   source->dirty = TRUE;
  1536.                }
  1537.             }
  1538.  
  1539.             /*
  1540.              * do actions that may require deleting from file
  1541.              */
  1542.             if (action == MOVE || action == DELETE) {
  1543.                lens = find_end( source->line, source->len );
  1544.                if (lens >= (bc + 1)) {
  1545.                   source->dirty = TRUE;
  1546.                   add = block_len;
  1547.                   xbc = bc;
  1548.                   if (lens <= (ec + 1))
  1549.                      add = lens - bc;
  1550.                   if (same && action == MOVE) {
  1551.                      if (rcol < bc && rline >= br && rline <=er)
  1552.                         if (li >= rline) {
  1553.                            xbc = bc + block_len;
  1554.                            if (lens <= (ec + block_len + 1))
  1555.                               add = lens - xbc;
  1556.                         }
  1557.                   }
  1558.                   if (add > 0)
  1559.                      *rc = delete_box_block( &s_w, source, xbc, add );
  1560.                }
  1561.             }
  1562.          }
  1563.  
  1564.          /*
  1565.           * if we are doing any BOX action we need to move the source pointer
  1566.           * to the next line.
  1567.           */
  1568.          source = source->next;
  1569.  
  1570.          /*
  1571.           * if we are doing any action other than DELETE, we need to move
  1572.           * the destination to the next line in marked block.
  1573.           * In BOX mode, we may need to pad the end of the file
  1574.           * with a blank line before we process the next line.
  1575.           */
  1576.          if (action != DELETE && action != FILL && action != NUMBER) {
  1577.             p = dest->next;
  1578.             if (p->len != EOF)
  1579.                dest = p;
  1580.             else if (li < er) {
  1581.                padded_file = TRUE;
  1582.                pad_dest_line( window, dest_file, p );
  1583.                dest = dest->next;
  1584.             }
  1585.          }
  1586.       }
  1587.    }
  1588.    if (block_buff != NULL)
  1589.       free( block_buff );
  1590.    if (swap_buff != NULL)
  1591.       free( swap_buff );
  1592.    if (padded_file) {
  1593.       w = g_status.window_list;
  1594.       while (w != NULL) {
  1595.          if (w->file_info == dest_file  &&  w->visible)
  1596.             show_size( w );
  1597.          w = w->next;
  1598.       }
  1599.    }
  1600.    show_avail_mem( );
  1601. }
  1602.  
  1603.  
  1604. /*
  1605.  * Name:    load_box_buff
  1606.  * Class:   helper function
  1607.  * Purpose: copy the contents of a BOX to a block buffer.
  1608.  * Date:    June 5, 1991
  1609.  * Passed:  block_buff: local buffer for block moves
  1610.  *          ll:         node to source line in file to load
  1611.  *          bc:     beginning column of BOX. used only in BOX operations.
  1612.  *          ec:     ending column of BOX. used only in BOX operations.
  1613.  *          filler: character to fill boxes that end past eol
  1614.  * Notes:   For BOX blocks, there are several things to take care of:
  1615.  *            1) The BOX begins and ends within a line - just copy the blocked
  1616.  *            characters to the block buff.  2) the BOX begins within a line
  1617.  *            but ends past the eol - copy all the characters within the line
  1618.  *            to the block buff then fill with padding.  3) the BOX begins and
  1619.  *            ends past eol - fill entire block buff with padding (filler).
  1620.  *          the fill character varies with the block operation.  for sorting
  1621.  *            a box block, the fill character is '\0'.  for adding text to
  1622.  *            the file, the fill character is a space.
  1623.  */
  1624. void load_box_buff( char *block_buff, line_list_ptr ll, int bc, int ec,
  1625.                     char filler )
  1626. {
  1627. int len;
  1628. int avlen;
  1629. register int i;
  1630. register char *bb;
  1631. text_ptr s;
  1632.  
  1633.    assert( bc >= 0 );
  1634.    assert( ec >= bc );
  1635.    assert( ec < MAX_LINE_LENGTH );
  1636.  
  1637.    bb = block_buff;
  1638.    len = ll->len;
  1639.    s = detab_a_line( ll->line, &len );
  1640.    /*
  1641.     * block start may be past eol
  1642.     */
  1643.    if (len < ec + 1) {
  1644.       /*
  1645.        * does block start past eol? - fill with pad
  1646.        */
  1647.       assert( ec + 1 - bc >= 0 );
  1648.  
  1649.       memset( block_buff, filler, (ec + 1) - bc );
  1650.       if (len >= bc) {
  1651.          /*
  1652.           * block ends past eol - fill with pad
  1653.           */
  1654.          avlen = len - bc;
  1655.          s += bc;
  1656.          for (i=avlen; i>0; i--)
  1657.             *bb++ = *s++;
  1658.       }
  1659.    } else {
  1660.       /*
  1661.        * block is within line - copy block to buffer
  1662.        */
  1663.       avlen = (ec + 1) - bc;
  1664.       s = s + bc;
  1665.       for (i=avlen; i>0; i--)
  1666.          *bb++ = *s++;
  1667.    }
  1668. }
  1669.  
  1670.  
  1671. /*
  1672.  * Name:    copy_buff_2file
  1673.  * Class:   helper function
  1674.  * Purpose: copy the contents of block buffer to destination file
  1675.  * Date:    June 5, 1991
  1676.  * Passed:  window:     pointer to current window
  1677.  *          block_buff: local buffer for moves
  1678.  *          dest:       pointer to destination line in destination file
  1679.  *          rcol:       if in BOX mode, destination column in destination file
  1680.  *          block_len:  if in BOX mode, width of block to copy
  1681.  *          action:     type of block action
  1682.  * Notes:   In BOX mode, the destination line has already been prepared.
  1683.  *          Just copy the BOX buffer to the destination line.
  1684.  */
  1685. int  copy_buff_2file( WINDOW *window, char *block_buff, line_list_ptr dest,
  1686.                       int rcol, int block_len, int action )
  1687. {
  1688. char *s;
  1689. char *d;
  1690. int len;
  1691. int pad;
  1692. int add;
  1693.  
  1694.    copy_line( dest );
  1695.    if (mode.inflate_tabs)
  1696.       detab_linebuff( );
  1697.  
  1698.    len = g_status.line_buff_len;
  1699.  
  1700.    assert( len >= 0 );
  1701.    assert( len < MAX_LINE_LENGTH );
  1702.    assert( rcol >= 0 );
  1703.    assert( rcol < MAX_LINE_LENGTH );
  1704.    assert( block_len >= 0 );
  1705.    assert( block_len < BUFF_SIZE );
  1706.  
  1707.    if (rcol > len) {
  1708.       pad = rcol - len;
  1709.  
  1710.       assert( pad >= 0 );
  1711.       assert( pad < MAX_LINE_LENGTH );
  1712.  
  1713.       memset( g_status.line_buff + len, ' ', pad );
  1714.       len += pad;
  1715.    }
  1716.  
  1717.    s = g_status.line_buff + rcol;
  1718.  
  1719.    /*
  1720.     * s is pointing to location to perform BOX operation.  If we do a
  1721.     * FILL or OVERLAY, we do not necessarily add any extra space.  If the
  1722.     * line does not extend all the thru the BOX then we add.
  1723.     * we always add space when we COPY, KOPY, or MOVE
  1724.     */
  1725.    if (action == FILL || action == OVERLAY || action == NUMBER || action == SWAP) {
  1726.       add = len - rcol;
  1727.       if (add < block_len) {
  1728.          pad = block_len - add;
  1729.  
  1730.          assert( pad >= 0 );
  1731.          assert( pad < MAX_LINE_LENGTH );
  1732.  
  1733.          memset( g_status.line_buff + len, ' ', pad );
  1734.          len += pad;
  1735.       }
  1736.    } else {
  1737.       d = s + block_len;
  1738.       add = len - rcol;
  1739.  
  1740.       assert( add >= 0 );
  1741.       assert( add < MAX_LINE_LENGTH );
  1742.  
  1743.       memmove( d, s, add );
  1744.       len += block_len;
  1745.    }
  1746.  
  1747.    assert( rcol + block_len <= len );
  1748.    assert( len >= 0 );
  1749.    assert( len < MAX_LINE_LENGTH );
  1750.  
  1751.    memmove( s, block_buff, block_len );
  1752.    g_status.line_buff_len = len;
  1753.    if (mode.inflate_tabs)
  1754.       entab_linebuff( );
  1755.    return( un_copy_line( dest, window, TRUE ) );
  1756. }
  1757.  
  1758.  
  1759. /*
  1760.  * Name:    block_fill
  1761.  * Class:   helper function
  1762.  * Purpose: fill the block buffer with character
  1763.  * Date:    June 5, 1991
  1764.  * Passed:  block_buff: local buffer for moves
  1765.  *          fill_char:  fill character
  1766.  *          block_len:  number of columns in block
  1767.  * Notes:   Fill block_buffer for block_len characters using fill_char.  This
  1768.  *          function is used only for BOX blocks.
  1769.  */
  1770. void block_fill( char *block_buff, int fill_char, int block_len )
  1771. {
  1772.    assert( block_len >= 0 );
  1773.    assert( block_len < BUFF_SIZE );
  1774.    assert( block_buff != NULL );
  1775.  
  1776.    memset( block_buff, fill_char, block_len );
  1777. }
  1778.  
  1779.  
  1780. /*
  1781.  * Name:    number_block_buff
  1782.  * Class:   helper function
  1783.  * Purpose: put a number into the block buffer
  1784.  * Date:    June 5, 1991
  1785.  * Passed:  block_buff: local buffer for moves
  1786.  *          block_len:  number of columns in block
  1787.  *          block_num:  long number to fill block
  1788.  *          just:       LEFT or RIGHT justified?
  1789.  * Notes:   Fill block_buffer for block_len characters with number.
  1790.  *          This function is used only for BOX blocks.
  1791.  */
  1792. void number_block_buff( char *block_buff, int block_len, long block_num,
  1793.                         int just )
  1794. {
  1795. int len;                /* length of number buffer */
  1796. int i;
  1797. char temp[MAX_COLS];    /* buffer for long number to ascii conversion  */
  1798.  
  1799.    assert( block_len >= 0 );
  1800.    assert( block_len < BUFF_SIZE );
  1801.  
  1802.    block_fill( block_buff, ' ', block_len );
  1803.    len = strlen( ltoa( block_num, temp, 10 ) );
  1804.    if (just == RIGHT) {
  1805.       block_len--;
  1806.       len--;
  1807.       for (;block_len >= 0 && len >= 0; block_len--, len--)
  1808.          block_buff[block_len] = temp[len];
  1809.    } else {
  1810.       for (i=0; block_len > 0 && i < len; block_len--, i++)
  1811.          block_buff[i] = temp[i];
  1812.    }
  1813. }
  1814.  
  1815.  
  1816. /*
  1817.  * Name:    restore_cursors
  1818.  * Class:   helper function
  1819.  * Purpose: a file has been modified - must restore all cursor pointers
  1820.  * Date:    June 5, 1991
  1821.  * Passed:  file:  pointer to file with changes
  1822.  * Notes:   Go through the window list and adjust the cursor pointers
  1823.  *          as needed.
  1824.  */
  1825. void restore_cursors( file_infos *file )
  1826. {
  1827. register WINDOW *window;
  1828. line_list_ptr ll;
  1829. long n;
  1830.  
  1831.    assert( file != NULL );
  1832.  
  1833.    window = g_status.window_list;
  1834.    while (window != NULL) {
  1835.       if (window->file_info == file) {
  1836.          window->bin_offset = 0;
  1837.          if (window->rline < 1L)
  1838.             window->rline = 1L;
  1839.          if (window->rline > file->length)
  1840.             window->rline = file->length;
  1841.          ll = file->line_list;
  1842.          n = 1L;
  1843.          for (; n < window->rline; n++) {
  1844.             window->bin_offset += ll->len;
  1845.             ll = ll->next;
  1846.          }
  1847.          window->ll = ll;
  1848.          if (window->rline < (window->cline - (window->top_line+window->ruler-1)))
  1849.             window->cline = (int)window->rline + window->top_line+window->ruler-1;
  1850.          if (window->cline < window->top_line + window->ruler)
  1851.             window->cline = window->top_line + window->ruler;
  1852.          if (window->visible)
  1853.             show_size( window );
  1854.       }
  1855.       window = window->next;
  1856.    }
  1857. }
  1858.  
  1859.  
  1860. /*
  1861.  * Name:    delete_box_block
  1862.  * Class:   helper function
  1863.  * Purpose: delete the marked text
  1864.  * Date:    June 5, 1991
  1865.  * Passed:  s_w:    source window
  1866.  *          source: pointer to line with block to delete
  1867.  *          bc:     beginning column of block - BOX mode only
  1868.  *          add:    number of characters in block to delete
  1869.  * Notes:   Used only for BOX blocks.  Delete the block.
  1870.  */
  1871. int  delete_box_block( WINDOW *s_w, line_list_ptr source, int bc, int add )
  1872. {
  1873. char *s;
  1874. int number;
  1875.  
  1876.    assert( s_w != NULL );
  1877.    assert( source != NULL );
  1878.    assert( bc >= 0 );
  1879.    assert( bc < MAX_LINE_LENGTH );
  1880.    assert( add >= 0 );
  1881.    assert( add < MAX_LINE_LENGTH );
  1882.  
  1883.    copy_line( source );
  1884.    detab_linebuff( );
  1885.    number = g_status.line_buff_len - bc;
  1886.    s = g_status.line_buff + bc + add;
  1887.  
  1888.    assert( number >= 0 );
  1889.    assert( number < MAX_LINE_LENGTH );
  1890.    assert( bc + add >= 0 );
  1891.    assert( bc + add < MAX_LINE_LENGTH );
  1892.    assert( add <= g_status.line_buff_len );
  1893.  
  1894.    memmove( s - add, s, number );
  1895.    g_status.line_buff_len -= add;
  1896.    entab_linebuff( );
  1897.    return( un_copy_line( source, s_w, TRUE ) );
  1898. }
  1899.  
  1900.  
  1901. /*
  1902.  * Name:    check_block
  1903.  * Class:   helper function
  1904.  * Purpose: To check that the block is still valid.
  1905.  * Date:    June 5, 1991
  1906.  * Notes:   After some editing, the marked block may not be valid.  For example,
  1907.  *          deleting all the lines in a block in another window.  We don't
  1908.  *          need to keep up with the block text pointers while doing normal
  1909.  *          editing; however, we need to refresh them before doing block stuff.
  1910.  */
  1911. void check_block( void )
  1912. {
  1913. register file_infos *file;
  1914. WINDOW filler;
  1915.  
  1916.    file = g_status.marked_file;
  1917.    if (file == NULL || file->block_br > file->length)
  1918.       unmark_block( &filler );
  1919.    else {
  1920.       if (file->block_er > file->length)
  1921.          file->block_er = file->length;
  1922.       find_begblock( file );
  1923.       find_endblock( file );
  1924.    }
  1925. }
  1926.  
  1927.  
  1928. /*
  1929.  * Name:    find_begblock
  1930.  * Class:   helper editor function
  1931.  * Purpose: find the beginning line in file with marked block
  1932.  * Date:    June 5, 1991
  1933.  * Passed:  file: file containing marked block
  1934.  * Notes:   file->block_start contains starting line of marked block.
  1935.  */
  1936. void find_begblock( file_infos *file )
  1937. {
  1938. line_list_ptr ll;
  1939. long li;           /* line counter (long i) */
  1940.  
  1941.    assert( file != NULL );
  1942.    assert( file->line_list != NULL );
  1943.  
  1944.    ll = file->line_list;
  1945.    for (li=1; li<file->block_br && ll->next != NULL; li++)
  1946.       ll = ll->next;
  1947.  
  1948.    file->block_start = ll;
  1949. }
  1950.  
  1951.  
  1952. /*
  1953.  * Name:    find_endblock
  1954.  * Class:   helper function
  1955.  * Purpose: find the ending line in file with marked block
  1956.  * Date:    June 5, 1991
  1957.  * Passed:  file: file containing marked block
  1958.  * Notes:   If in LINE mode, file->block_end is set to end of line of last
  1959.  *          line in block.  If in BOX mode, file->block_end is set to
  1960.  *          beginning of last line in marked block.  If the search for the
  1961.  *          ending line of the marked block goes past the eof, set the
  1962.  *          ending line of the block to the last line in the file.
  1963.  */
  1964. void find_endblock( file_infos *file )
  1965. {
  1966. line_list_ptr ll; /* start from beginning of file and go to end */
  1967. long i;           /* line counter */
  1968. register file_infos *fp;
  1969.  
  1970.    assert( file != NULL );
  1971.    assert( file->block_start != NULL );
  1972.  
  1973.    fp = file;
  1974.    ll = fp->block_start;
  1975.    if (ll != NULL) {
  1976.       for (i=fp->block_br;  i < fp->block_er && ll->next != NULL; i++)
  1977.          ll = ll->next;
  1978.       if (ll != NULL)
  1979.          fp->block_end = ll;
  1980.       else {
  1981.  
  1982.          /*
  1983.           * last line in marked block is NULL.  if LINE block, set end to
  1984.           * last character in the file.  if STREAM or BOX block, set end to
  1985.           * start of last line in file.  ending row, or er, is then set to
  1986.           * file length.
  1987.           */
  1988.          fp->block_end = fp->line_list_end->prev;
  1989.          fp->block_er = fp->length;
  1990.       }
  1991.    }
  1992. }
  1993.  
  1994.  
  1995. /*
  1996.  * Name:    block_write
  1997.  * Class:   primary editor function
  1998.  * Purpose: To write the currently marked block to a disk file.
  1999.  * Date:    June 5, 1991
  2000.  * Passed:  window:  pointer to current window
  2001.  * Notes:   If the file already exists, the user gets to choose whether
  2002.  *           to overwrite or append.
  2003.  */
  2004. int  block_write( WINDOW *window )
  2005. {
  2006. int prompt_line;
  2007. int rc;
  2008. char buff[MAX_COLS+2]; /* buffer for char and attribute  */
  2009. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  2010. file_infos *file;
  2011. int block_type;
  2012. int fattr;
  2013.  
  2014.    /*
  2015.     * make sure block is marked OK
  2016.     */
  2017.    entab_linebuff( );
  2018.    rc = un_copy_line( window->ll, window, TRUE );
  2019.    check_block( );
  2020.    if (rc == OK  &&  g_status.marked == TRUE) {
  2021.       prompt_line = window->bottom_line;
  2022.       file        = g_status.marked_file;
  2023.  
  2024.       assert( file != NULL );
  2025.  
  2026.       block_type  = file->block_type;
  2027.  
  2028.       /*
  2029.        * find out which file to write to
  2030.        */
  2031.       save_screen_line( 0, prompt_line, line_buff );
  2032.       *g_status.rw_name = '\0';
  2033.       if (get_name( block6, prompt_line, g_status.rw_name,
  2034.                     g_display.message_color ) == OK) {
  2035.          /*
  2036.           * if the file exists, find out whether to overwrite or append
  2037.           */
  2038.          rc = get_fattr( g_status.rw_name, &fattr );
  2039.          if (rc == OK) {
  2040.             /*
  2041.              * file exists. overwrite or append?
  2042.              */
  2043.             set_prompt( block7, prompt_line );
  2044.             switch (get_oa( )) {
  2045.                case A_OVERWRITE :
  2046.                   change_mode( g_status.rw_name, prompt_line );
  2047.                   /*
  2048.                    * writing block to
  2049.                    */
  2050.                   combine_strings( buff, block8, g_status.rw_name, "'" );
  2051.                   s_output( buff, prompt_line, 0, g_display.message_color );
  2052.                   rc = hw_save( g_status.rw_name, file, file->block_br,
  2053.                                 file->block_er, block_type );
  2054.                   if (rc == ERROR)
  2055.                      /*
  2056.                       * could not write block
  2057.                       */
  2058.                      error( WARNING, prompt_line, block9 );
  2059.                   break;
  2060.                case A_APPEND :
  2061.                   /*
  2062.                    * appending block to
  2063.                    */
  2064.                   combine_strings( buff, block10, g_status.rw_name, "'" );
  2065.                   s_output( buff, prompt_line, 0, g_display.message_color );
  2066.                   rc = hw_append( g_status.rw_name, file, file->block_br,
  2067.                                   file->block_er, block_type );
  2068.                   if (rc == ERROR)
  2069.                      /*
  2070.                       * could not append block
  2071.                       */
  2072.                      error( WARNING, prompt_line, block11 );
  2073.                   break;
  2074.                case AbortCommand :
  2075.                default :
  2076.                   rc = ERROR;
  2077.                   break;
  2078.             }
  2079.          } else if (rc != ERROR) {
  2080.             /*
  2081.              * writing block to
  2082.              */
  2083.             combine_strings( buff, block12, g_status.rw_name, "'" );
  2084.             s_output( buff, prompt_line, 0, g_display.message_color );
  2085.             if (hw_save( g_status.rw_name, file, file->block_br, file->block_er,
  2086.                          block_type ) == ERROR) {
  2087.                /*
  2088.                 * could not write block
  2089.                 */
  2090.                error( WARNING, prompt_line, block9 );
  2091.                rc = ERROR;
  2092.             }
  2093.          }
  2094.       }
  2095.       restore_screen_line( 0, prompt_line, line_buff );
  2096.    } else
  2097.       rc = ERROR;
  2098.    return( rc );
  2099. }
  2100.  
  2101.  
  2102. /*
  2103.  * Name:    block_print
  2104.  * Class:   primary editor function
  2105.  * Purpose: Print an entire file or the currently marked block.
  2106.  * Date:    June 5, 1991
  2107.  * Passed:  window:  pointer to current window
  2108.  * Notes:   With the added Critical Error Handler routine, let's fflush
  2109.  *          the print buffer first.
  2110.  */
  2111. int  block_print( WINDOW *window )
  2112. {
  2113. char answer[MAX_COLS];          /* entire file or just marked block? */
  2114. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  2115. int  col;
  2116. int  func;
  2117. int  prompt_line;
  2118. line_list_ptr block_start;   /* start of block in file */
  2119. file_infos *file;
  2120. int  block_type;
  2121. char *p;
  2122. int  len;
  2123. int  bc;
  2124. int  ec;
  2125. int  last_c;
  2126. long lbegin;
  2127. long lend;
  2128. long l;
  2129. int  color;
  2130. int  rc;
  2131.  
  2132.    color = g_display.message_color;
  2133.    entab_linebuff( );
  2134.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  2135.       return( ERROR );
  2136.    rc = OK;
  2137.    prompt_line = window->bottom_line;
  2138.    save_screen_line( 0, prompt_line, line_buff );
  2139.    /*
  2140.     * print entire file or just marked block?
  2141.     */
  2142.  
  2143.    assert( strlen( block13 ) < MAX_COLS );
  2144.  
  2145.    strcpy( answer, block13 );
  2146.    col = strlen( answer );
  2147.    s_output( answer, prompt_line, 0, color );
  2148.    eol_clear( col, prompt_line, g_display.text_color );
  2149.    xygoto( col, prompt_line );
  2150.    func = col = 0;
  2151.    while (col != 'f' && col != 'F' && col != 'b' && col != 'B' &&
  2152.           func != AbortCommand) {
  2153.       col = getkey( );
  2154.       func = getfunc( col );
  2155.       if (col == ESC  ||  func == AbortCommand)
  2156.          rc = ERROR;
  2157.    }
  2158.  
  2159.    if (rc == OK) {
  2160.       /*
  2161.        * if everything is everything, flush the printer before we start
  2162.        *   printing.  then, check the critical error flag after the flush.
  2163.        */
  2164.       fflush( stdprn );
  2165.       if (ceh.flag == ERROR)
  2166.          rc = ERROR;
  2167.    }
  2168.  
  2169.    if (rc != ERROR) {
  2170.       file = window->file_info;
  2171.       block_type  = NOTMARKED;
  2172.       if (col == 'f' || col == 'F') {
  2173.          block_start = file->line_list;
  2174.          lend =   l  = file->length;
  2175.       } else {
  2176.          check_block( );
  2177.          if (g_status.marked == TRUE) {
  2178.             file        = g_status.marked_file;
  2179.             block_start = file->block_start;
  2180.             block_type  = file->block_type;
  2181.             lend =   l  = file->block_er + 1l - file->block_br;
  2182.          } else
  2183.             rc = ERROR;
  2184.       }
  2185.  
  2186.       if (rc != ERROR) {
  2187.          eol_clear( 0, prompt_line, color );
  2188.          /*
  2189.           * printing line   of    press control-break to cancel.
  2190.           */
  2191.          s_output( block14, prompt_line, 0, color );
  2192.          ltoa( l, answer, 10 );
  2193.          s_output( answer, prompt_line, 25, color );
  2194.          xygoto( 14, prompt_line );
  2195.          if (block_type == BOX || block_type == STREAM) {
  2196.             bc = file->block_bc;
  2197.             ec = file->block_ec;
  2198.             last_c = ec + 1 - bc;
  2199.          }
  2200.          p = g_status.line_buff;
  2201.          lbegin = 1;
  2202.          for (col=OK; l>0 && col == OK && !g_status.control_break; l--) {
  2203.             ltoa( lbegin, answer, 10 );
  2204.             s_output( answer, prompt_line, 14, color );
  2205.             g_status.copied = FALSE;
  2206.             if (block_type == BOX) {
  2207.                load_box_buff( p, block_start, bc, ec, ' ' );
  2208.                len = last_c;
  2209.             } else if (block_type == STREAM && lbegin == 1) {
  2210.                len = block_start->len;
  2211.                detab_a_line( block_start->line, &len );
  2212.                if (bc > len)
  2213.                   len = 0;
  2214.                else {
  2215.                   if (lbegin == lend) {
  2216.                      load_box_buff( p, block_start, bc, ec, ' ' );
  2217.                      len = last_c;
  2218.                   } else {
  2219.                      len = len - bc;
  2220.                      g_status.copied = TRUE;
  2221.  
  2222.                      assert( len >= 0 );
  2223.                      assert( len < MAX_LINE_LENGTH );
  2224.  
  2225.                      _fmemcpy( p, block_start->line + bc, len );
  2226.                   }
  2227.                }
  2228.             } else if (block_type == STREAM && l == 1L) {
  2229.                copy_line( block_start );
  2230.                detab_linebuff( );
  2231.                len = g_status.line_buff_len;
  2232.                if (len > ec + 1)
  2233.                   len = ec + 1;
  2234.             } else {
  2235.                copy_line( block_start );
  2236.                len = g_status.line_buff_len;
  2237.             }
  2238.  
  2239.             assert( len >= 0 );
  2240.             assert( len < MAX_LINE_LENGTH );
  2241.  
  2242.             *(p+len) = '\r';
  2243.             ++len;
  2244.             *(p+len) = '\n';
  2245.             ++len;
  2246.             if (fwrite( p, sizeof( char ), len, stdprn ) < (unsigned)len ||
  2247.                 ceh.flag == ERROR)
  2248.                col = ERROR;
  2249.             block_start = block_start->next;
  2250.             ++lbegin;
  2251.          }
  2252.          g_status.copied = FALSE;
  2253.          if (ceh.flag != ERROR)
  2254.             fflush( stdprn );
  2255.          else
  2256.             rc = ERROR;
  2257.       }
  2258.    }
  2259.    g_status.copied = FALSE;
  2260.    restore_screen_line( 0, prompt_line, line_buff );
  2261.    return( rc );
  2262. }
  2263.  
  2264.  
  2265. /*
  2266.  * Name:    get_block_fill_char
  2267.  * Class:   helper function
  2268.  * Purpose: get the character to fill marked block.
  2269.  * Date:    June 5, 1991
  2270.  * Passed:  window:  pointer to current window
  2271.  *          c: address of character to fill block
  2272.  */
  2273. int  get_block_fill_char( WINDOW *window, int *c )
  2274. {
  2275. char answer[MAX_COLS];
  2276. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  2277. register int col;
  2278. int prompt_line;
  2279. int rc;
  2280.  
  2281.    rc = OK;
  2282.    prompt_line = window->bottom_line;
  2283.    save_screen_line( 0, prompt_line, line_buff );
  2284.    /*
  2285.     * enter character to file block (esc to exit)
  2286.     */
  2287.  
  2288.    assert( strlen( block15 ) < MAX_COLS );
  2289.  
  2290.    strcpy( answer, block15 );
  2291.    s_output( answer, prompt_line, 0, g_display.message_color );
  2292.    col = strlen( answer );
  2293.    eol_clear( col, prompt_line, g_display.text_color );
  2294.    xygoto( col, prompt_line );
  2295.    col = getkey( );
  2296.    if (col >= 256)
  2297.       rc = ERROR;
  2298.    else
  2299.       *c = col;
  2300.    restore_screen_line( 0, prompt_line, line_buff );
  2301.    return( rc );
  2302. }
  2303.  
  2304.  
  2305. /*
  2306.  * Name:    get_block_numbers
  2307.  * Class:   helper function
  2308.  * Purpose: get the starting number and increment
  2309.  * Date:    June 5, 1991
  2310.  * Passed:  window:  pointer to current window
  2311.  *          block_num: address of number to start numbering
  2312.  *          block_inc: address of number to add to block_num
  2313.  *          just:      left or right justify numbers in block?
  2314.  */
  2315. int  get_block_numbers( WINDOW *window, long *block_num, long *block_inc,
  2316.                         int *just )
  2317. {
  2318. char answer[MAX_COLS];
  2319. int prompt_line;
  2320. register int rc;
  2321. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  2322. register int col;
  2323.  
  2324.    prompt_line = window->bottom_line;
  2325.  
  2326.    /*
  2327.     * don't assume anything on starting number - start w/ null string.
  2328.     */
  2329.    answer[0] = '\0';
  2330.    /*
  2331.     * enter starting number
  2332.     */
  2333.    rc = get_name( block16, prompt_line, answer, g_display.message_color );
  2334.    if (answer[0] == '\0')
  2335.       rc = ERROR;
  2336.    if (rc != ERROR) {
  2337.       *block_num = atol( answer );
  2338.  
  2339.       /*
  2340.        * assume increment is 1
  2341.        */
  2342.       answer[0] = '1';
  2343.       answer[1] = '\0';
  2344.       /*
  2345.        * enter increment
  2346.        */
  2347.       rc = get_name( block17, prompt_line, answer, g_display.message_color );
  2348.       if (answer[0] == '\0')
  2349.          rc = ERROR;
  2350.       if (rc != ERROR) {
  2351.          *block_inc = atol( answer );
  2352.  
  2353.          /*
  2354.           * now, get left or right justification.  save contents of screen
  2355.           *  in a buffer, then write contents of buffer back to screen when
  2356.           *  we get through w/ justification.
  2357.           */
  2358.          save_screen_line( 0, prompt_line, line_buff );
  2359.          /*
  2360.           * left or right justify (l/r)?
  2361.           */
  2362.  
  2363.          assert( strlen( block18 ) < MAX_COLS );
  2364.  
  2365.          strcpy( answer, block18 );
  2366.          s_output( answer, prompt_line, 0, g_display.message_color );
  2367.          col = strlen( answer );
  2368.          eol_clear( col, prompt_line, g_display.text_color );
  2369.          xygoto( col, prompt_line );
  2370.          rc = get_lr( );
  2371.          if (rc != ERROR) {
  2372.             *just = rc;
  2373.             rc = OK;
  2374.          }
  2375.          restore_screen_line( 0, prompt_line, line_buff );
  2376.       }
  2377.    }
  2378.  
  2379.    /*
  2380.     * if everything is everything then return code = OK.
  2381.     */
  2382.    return( rc );
  2383. }
  2384.  
  2385.  
  2386. /*
  2387.  * Name:    block_trim_trailing
  2388.  * Class:   primary editor function
  2389.  * Purpose: Trim trailing space in a LINE block.
  2390.  * Date:    June 5, 1991
  2391.  * Passed:  window:  pointer to current window
  2392.  * Notes:   Use copy_line and un_copy_line to do the work.
  2393.  */
  2394. int  block_trim_trailing( WINDOW *window )
  2395. {
  2396. int prompt_line;
  2397. int rc;
  2398. line_list_ptr p;             /* pointer to block line */
  2399. file_infos *file;
  2400. WINDOW *sw, s_w;
  2401. long er;
  2402. int  trailing;               /* save trailing setting */
  2403.  
  2404.    /*
  2405.     * make sure block is marked OK and that this is a LINE block
  2406.     */
  2407.    prompt_line = window->bottom_line;
  2408.    entab_linebuff( );
  2409.    rc = un_copy_line( window->ll, window, TRUE );
  2410.    check_block( );
  2411.    if (rc != ERROR && g_status.marked == TRUE) {
  2412.  
  2413.       trailing = mode.trailing;
  2414.       mode.trailing = TRUE;
  2415.       file = g_status.marked_file;
  2416.       if (file->block_type != LINE) {
  2417.          /*
  2418.           * can only trim trailing space in line blocks
  2419.           */
  2420.          error( WARNING, prompt_line, block21 );
  2421.          return( ERROR );
  2422.       }
  2423.  
  2424.       /*
  2425.        * initialize everything
  2426.        */
  2427.       sw = g_status.window_list;
  2428.       for (; ptoul( sw->file_info ) != ptoul( file );)
  2429.          sw = sw->next;
  2430.       if (mode.do_backups == TRUE) {
  2431.          file->modified = TRUE;
  2432.          rc = backup_file( sw );
  2433.       }
  2434.       dup_window_info( &s_w, sw );
  2435.  
  2436.       /*
  2437.        * set window to invisible so the un_copy_line function will
  2438.        * not display the lines while trimming.
  2439.        */
  2440.       s_w.visible = FALSE;
  2441.  
  2442.       p  = file->block_start;
  2443.       er = file->block_er;
  2444.       s_w.rline = file->block_br;
  2445.       for (; rc == OK && s_w.rline <= er  &&  !g_status.control_break; s_w.rline++) {
  2446.  
  2447.          /*
  2448.           * use the line buffer to trim space.
  2449.           */
  2450.          copy_line( p );
  2451.          rc = un_copy_line( p, &s_w, TRUE );
  2452.          p = p->next;
  2453.       }
  2454.  
  2455.       /*
  2456.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  2457.        * not necessarily be on the last line of the block.
  2458.        */
  2459.       g_status.copied = FALSE;
  2460.       file->dirty = GLOBAL;
  2461.       mode.trailing = trailing;
  2462.       show_avail_mem( );
  2463.    }
  2464.    return( rc );
  2465. }
  2466.  
  2467.  
  2468. /*
  2469.  * Name:    block_email_reply
  2470.  * Class:   primary editor function
  2471.  * Purpose: insert the standard replay character '>' at beginning of line
  2472.  * Date:    June 5, 1992
  2473.  * Passed:  window:  pointer to current window
  2474.  * Notes:   it is customary to prepend "> " to the initial text and
  2475.  *             ">" to replies to replies to etc...
  2476.  */
  2477. int  block_email_reply( WINDOW *window )
  2478. {
  2479. int prompt_line;
  2480. int add;
  2481. int len;
  2482. int rc;
  2483. char *source;    /* source for block move to make room for c */
  2484. char *dest;      /* destination for block move */
  2485. line_list_ptr p;                     /* pointer to block line */
  2486. file_infos *file;
  2487. WINDOW *sw, s_w;
  2488. long er;
  2489.  
  2490.    /*
  2491.     * make sure block is marked OK and that this is a LINE block
  2492.     */
  2493.    prompt_line = window->bottom_line;
  2494.    entab_linebuff( );
  2495.    rc = un_copy_line( window->ll, window, TRUE );
  2496.    check_block( );
  2497.    if (rc != ERROR  &&  g_status.marked == TRUE) {
  2498.       file = g_status.marked_file;
  2499.       if (file->block_type != LINE) {
  2500.          /*
  2501.           * can only reply line blocks
  2502.           */
  2503.          error( WARNING, prompt_line, block25 );
  2504.          return( ERROR );
  2505.       }
  2506.  
  2507.       /*
  2508.        * find a window that points to the file with a marked block.
  2509.        */
  2510.       sw = g_status.window_list;
  2511.       for (; ptoul( sw->file_info ) != ptoul( file );)
  2512.          sw = sw->next;
  2513.       if (mode.do_backups == TRUE) {
  2514.          file->modified = TRUE;
  2515.          rc = backup_file( sw );
  2516.       }
  2517.  
  2518.       /*
  2519.        * use a local window structure to do the dirty work.  initialize
  2520.        *   the local window structure to the beginning of the marked
  2521.        *   block.
  2522.        */
  2523.       dup_window_info( &s_w, sw );
  2524.  
  2525.       /*
  2526.        * set s_w to invisible so the un_copy_line function will
  2527.        * not display the lines while doing block stuff.
  2528.        */
  2529.       s_w.visible = FALSE;
  2530.       s_w.rline = file->block_br;
  2531.       p  = file->block_start;
  2532.       er = file->block_er;
  2533.  
  2534.       /*
  2535.        * for each line in the marked block, prepend the reply character(s)
  2536.        */
  2537.       for (; rc == OK  &&  s_w.rline <= er  &&  !g_status.control_break;
  2538.                                                              s_w.rline++) {
  2539.  
  2540.          /*
  2541.           * put the line in the g_status.line_buff.  use add to count the
  2542.           *   number of characters to insert at the beginning of a line.
  2543.           *   the original reply uses "> ", while replies to replies use ">".
  2544.           */
  2545.          copy_line( p );
  2546.          if (*(p->line) == '>')
  2547.             add = 1;
  2548.          else
  2549.             add = 2;
  2550.  
  2551.          /*
  2552.           * see if the line has room to add the ">" character.  if there is
  2553.           *   room, move everything down to make room for the
  2554.           *   reply character(s).
  2555.           */
  2556.          len = g_status.line_buff_len;
  2557.          if (len + add < MAX_LINE_LENGTH) {
  2558.             source = g_status.line_buff;
  2559.             dest = source + add;
  2560.  
  2561.             assert( len >= 0 );
  2562.             assert( len < MAX_LINE_LENGTH );
  2563.             assert( len + add < MAX_LINE_LENGTH );
  2564.  
  2565.             memmove( dest, source, len );
  2566.             *source = '>';
  2567.             if (add > 1)
  2568.               *(source+1) = ' ';
  2569.             g_status.line_buff_len = len + add;
  2570.             rc = un_copy_line( p, &s_w, TRUE );
  2571.          }
  2572.          p = p->next;
  2573.          g_status.copied = FALSE;
  2574.       }
  2575.  
  2576.       /*
  2577.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  2578.        * not necessarily be on the last line of the block.
  2579.        */
  2580.       g_status.copied = FALSE;
  2581.       file->dirty = GLOBAL;
  2582.       show_avail_mem( );
  2583.    }
  2584.    return( OK );
  2585. }
  2586.  
  2587.  
  2588. /*
  2589.  * Name:    block_convert_case
  2590.  * Class:   primary editor function
  2591.  * Purpose: convert characters to lower case, upper case, strip hi bits,
  2592.  *          or e-mail functions
  2593.  * Date:    June 5, 1991
  2594.  * Passed:  window:  pointer to current window
  2595.  */
  2596. int  block_convert_case( WINDOW *window )
  2597. {
  2598. int  len;
  2599. int  block_type;
  2600. line_list_ptr begin;
  2601. register file_infos *file;
  2602. WINDOW *sw;
  2603. long number;
  2604. long er;
  2605. unsigned int count;
  2606. int  bc, ec;
  2607. int  block_len;
  2608. int  rc;
  2609. void (*char_func)( text_ptr, unsigned int );
  2610.  
  2611.    /*
  2612.     * make sure block is marked OK
  2613.     */
  2614.    entab_linebuff( );
  2615.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  2616.       return( ERROR );
  2617.    rc = OK;
  2618.    check_block( );
  2619.    if (g_status.marked == TRUE) {
  2620.  
  2621.       /*
  2622.        * set char_func() to the required block function in tdeasm.c
  2623.        */
  2624.       switch (g_status.command) {
  2625.          case BlockUpperCase  :
  2626.             char_func = upper_case;
  2627.             break;
  2628.          case BlockLowerCase  :
  2629.             char_func = lower_case;
  2630.             break;
  2631.          case BlockRot13      :
  2632.             char_func = rot13;
  2633.             break;
  2634.          case BlockFixUUE     :
  2635.             char_func = fix_uue;
  2636.             break;
  2637.          case BlockStripHiBit :
  2638.             char_func = strip_hi;
  2639.             break;
  2640.          default :
  2641.             return( ERROR );
  2642.       }
  2643.  
  2644.       file  = g_status.marked_file;
  2645.       file->modified = TRUE;
  2646.       if (mode.do_backups == TRUE) {
  2647.          sw = g_status.window_list;
  2648.          for (; ptoul( sw->file_info ) != ptoul( file );)
  2649.             sw = sw->next;
  2650.          rc = backup_file( sw );
  2651.       }
  2652.  
  2653.       if (rc == OK) {
  2654.          block_type = file->block_type;
  2655.          ec = file->block_ec;
  2656.  
  2657.          begin  = file->block_start;
  2658.  
  2659.          er = file->block_er;
  2660.          block_len = ec + 1 - file->block_bc;
  2661.          for (number=file->block_br; number <= er; number++) {
  2662.             begin->dirty = TRUE;
  2663.             count = len = begin->len;
  2664.             bc = 0;
  2665.             if (block_type == STREAM) {
  2666.                if (number == file->block_br) {
  2667.                   bc = file->block_bc;
  2668.                   if (len < file->block_bc) {
  2669.                      count = 0;
  2670.                      bc = len;
  2671.                   }
  2672.                }
  2673.                if (number == file->block_er) {
  2674.                   if (ec < len)
  2675.                      ec = len;
  2676.                   count = ec - bc + 1;
  2677.                }
  2678.             } else if (block_type == BOX) {
  2679.                bc = file->block_bc;
  2680.                count =  len >= ec ? block_len : len - bc;
  2681.             }
  2682.             if (len > bc) {
  2683.  
  2684.                assert( count < MAX_LINE_LENGTH );
  2685.                assert( bc >= 0 );
  2686.                assert( bc < MAX_LINE_LENGTH );
  2687.  
  2688.                (*char_func)( begin->line+bc, count );
  2689.             }
  2690.             begin = begin->next;
  2691.          }
  2692.  
  2693.          /*
  2694.           * IMPORTANT:  we need to reset the copied flag because the cursor may
  2695.           * not necessarily be on the last line of the block.
  2696.           */
  2697.          g_status.copied = FALSE;
  2698.          file->dirty = GLOBAL;
  2699.       }
  2700.    } else
  2701.       rc = ERROR;
  2702.    return( rc );
  2703. }
  2704.  
  2705.  
  2706. /*
  2707.  * Name:    upper_case
  2708.  * Purpose: To convert all lower case characters to upper characters
  2709.  * Date:    June 5, 1991
  2710.  * Passed:  s:    the starting point
  2711.  *          count: number of characters to convert
  2712.  * Returns: none
  2713.  * Notes:   xor 0x20 with lower case to get upper case.  yes, I know
  2714.  *           the toupper( ) macro or function is faster, but let's
  2715.  *           let make it easy for users to modify for non-English alphabets.
  2716.  *          this routine only handles the English alphabet.  modify as
  2717.  *           needed for other alphabets.
  2718.  */
  2719. void upper_case( text_ptr s, size_t count )
  2720. {
  2721.    if (s != NULL) {
  2722.       for (; count > 0; s++, count-- ) {
  2723.          if (*s >= 'a'  &&  *s <= 'z')
  2724.             *s ^= 0x20;
  2725.       }
  2726.    }
  2727. }
  2728.  
  2729.  
  2730. /*
  2731.  * Name:    lower_case
  2732.  * Purpose: To convert all upper case characters to lower characters
  2733.  * Date:    June 5, 1991
  2734.  * Passed:  s:    the starting point
  2735.  *          count: number of characters to convert
  2736.  * Returns: none
  2737.  * Notes:   or upper case with 0x20 to get lower case.  yes, I know
  2738.  *           the tolower( ) macro or function is faster, but let's
  2739.  *           let make it easy for users to modify for non-English alphabets. 
  2740.  *          this routine only handles the English alphabet.  modify as
  2741.  *           needed for other alphabets.
  2742.  */
  2743. void lower_case( text_ptr s, size_t count )
  2744. {
  2745.    if (s != NULL) {
  2746.       for (; count > 0; s++, count-- ) {
  2747.          if (*s >= 'A'  &&  *s <= 'Z')
  2748.             *s |= 0x20;
  2749.       }
  2750.    }
  2751. }
  2752.  
  2753.  
  2754. /*
  2755.  * Name:    rot13
  2756.  * Purpose: To rotate all alphabet characters by 13
  2757.  * Date:    June 5, 1991
  2758.  * Passed:  s:    the starting point
  2759.  *          count: number of characters to convert
  2760.  * Returns: none
  2761.  * Notes:   simple rot13
  2762.  *          i really don't know how to handle rot13 for alphabets
  2763.  *           other than English.
  2764.  */
  2765. void rot13( text_ptr s, size_t count )
  2766. {
  2767. register size_t c;
  2768.  
  2769.    if (s != NULL) {
  2770.       for (; count > 0; s++, count-- ) {
  2771.          c = *s;
  2772.          if (c >= 'a'  &&  c <=  'm')
  2773.             c += 13;
  2774.          else if (c >= 'n'  &&  c <= 'z')
  2775.             c -= 13;
  2776.          else if (c >= 'A'  &&  c <=  'M')
  2777.             c += 13;
  2778.          else if (c >= 'N'  &&  c <= 'Z')
  2779.             c -= 13;
  2780.          *s = (unsigned char)c;
  2781.       }
  2782.    }
  2783. }
  2784.  
  2785.  
  2786. /*
  2787.  * Name:    fix_uue
  2788.  * Purpose: To fix EBCDIC ==> ASCII translation problem
  2789.  * Date:    June 5, 1991
  2790.  * Passed:  s:    the starting point
  2791.  *          count: number of characters to convert
  2792.  * Returns: none
  2793.  * Notes:   to fix the EBCDIC to ASCII translation problem, three characters
  2794.  *           need to be changed,  0x5d -> 0x7c, 0xd5 -> 0x5b, 0xe5 -> 0x5d
  2795.  */
  2796. void fix_uue( text_ptr s, size_t  count )
  2797. {
  2798.    if (s != NULL) {
  2799.       for (; count > 0; s++, count-- ) {
  2800.          switch (*s) {
  2801.             case 0x5d :
  2802.                *s = 0x7c;
  2803.                break;
  2804.             case 0xd5 :
  2805.                *s = 0x5b;
  2806.                break;
  2807.             case 0xe5 :
  2808.                *s = 0x5d;
  2809.                break;
  2810.             default :
  2811.                break;
  2812.          }
  2813.       }
  2814.    }
  2815. }
  2816.  
  2817.  
  2818. /*
  2819.  * Name:    strip_hi
  2820.  * Purpose: To strip bit 7 from characters
  2821.  * Date:    June 5, 1991
  2822.  * Passed:  s:    the starting point, which should be normalized to a segment
  2823.  *          count: number of characters to strip (size_t)
  2824.  *                 count should not be greater than MAX_LINE_LENGTH
  2825.  * Returns: none
  2826.  * Notes:   this function is useful on WordStar files.  to make a WordStar
  2827.  *           file readable, the hi bits need to be anded off.
  2828.  *          incidentally, soft CRs and LFs may be converted to hard CRs
  2829.  *           and LFs.  this function makes no attempt to split lines
  2830.  *           when soft returns are converted to hard returns.
  2831.  */
  2832. void strip_hi( text_ptr s, size_t count )
  2833. {
  2834.    if (s != NULL) {
  2835.       for (; count > 0; s++, count-- ) {
  2836.          if (*s >= 0x80)
  2837.             *s &= 0x7f;
  2838.       }
  2839.    }
  2840. }
  2841.